通过反射确定一个属性是否是数组类型

42

如何确定一个属性是否是数组类型。

示例:

public bool IsPropertyAnArray(PropertyInfo property)
{
    // return true if type is IList<T>, IEnumerable<T>, ObservableCollection<T>, etc...
}

1
所以你的意思是,如果该属性是一个集合?与您的评论匹配的内容不是数组。 - Ben Voigt
2
你可能是想用 IEnumerable 而不是 IEnumerator - vgru
@Ben:嗯,数组符合他的标准(IList<T>和IEnumerable<T>),但并不是所有符合他标准的东西都是数组。 :) - Sven
5个回答

77

您似乎在问两个不同的问题:一个类型是否是数组(例如string[]),或者任何集合类型。

对于前者,只需检查property.PropertyType.IsArray

对于后者,您必须决定要让类型符合的最低标准是什么。例如,您可以通过使用typeof(IEnumerable).IsAssignableFrom(property.PropertyType)来检查非泛型的IEnumerable。如果您知道T的实际类型,也可以将其用于通用接口,例如typeof(IEnumerable<int>).IsAssignableFrom(property.PropertyType)

如果不知道T的值,可以通过检查property.PropertyType.GetInterface(typeof(IEnumerable<>).FullName)是否不为null来检查通用的IEnumerable<T>或任何其他通用接口。请注意,在该代码中,我没有为T指定任何类型。您也可以对IList<T>或任何其他感兴趣的类型执行相同的操作。

例如,如果要检查通用的IEnumerable<T>,则可以使用以下内容:

public bool IsPropertyACollection(PropertyInfo property) 
{ 
    return property.PropertyType.GetInterface(typeof(IEnumerable<>).FullName) != null;
} 

数组也实现了IEnumerable接口,因此它们也会从该方法返回true


1
这个实现会缺少“老派集合”,它们继承自 .Net 1 并实现了 IEnumerable,但不是泛型版本的 IEnumerable<T>。它们越来越少被使用,但一些 API 仍然需要它们,而且它们真的很麻烦。 - Falanwe
1
@Falanwe:那就使用我提到的第一种方法,它确实会检查非泛型 IEnumerable。哪种方法最好取决于他的要求。 - Sven
1
我使用一个字符串属性尝试了你的方法,结果返回了true... 你知道为什么吗? - Melursus
6
String 类实现了 IEnumerable<char> 接口,因此符合这些标准可被视为一个集合。你需要具体地过滤它,或检查更具限制性的接口(例如 ICollection<T>IList<T>)。 - Sven
2
任何实现了通用的 IEnumerable<> 的类型也将成为非泛型的 IEnumerable - Jeppe Stig Nielsen
显示剩余2条评论

22

除了String类以外,因为它实现了IEnumerable<char>,所以也可以视为一种集合。

public bool IsPropertyACollection(this PropertyInfo property)
{
    return (!typeof(String).Equals(property.PropertyType) && 
        typeof(IEnumerable).IsAssignableFrom(property.PropertyType));
}

我缺少的是 typeof(String) 这个检查。 - Atul Chaudhary

19

如果你想知道一个属性是否为数组,实际上非常简单:

property.PropertyType.IsArray;

编辑

如果您想知道它是否是实现了IEnumerable接口的类型,就像所有“集合类型”一样,也不是很复杂:

return property.PropertyType.GetInterface("IEnumerable") != null;

我不确定这是否是 OP 真正想要的(但我承认命名有点误导)。实现 IList<T> 的自定义类型将从此属性返回 false。 - vgru
实际上,我最初误读了问题。我编辑了我的答案,包括任何IEnumerable。 - Falanwe
2
@Falanwe:IsSubclassOf 不适用于接口,只适用于基类。 - Sven
@Falanwe:我看不懂法语,但英文版说:“IsSubclassOf方法不能用于确定接口是否派生自另一个接口,或类是否实现了接口”(强调我的)。 - Sven
@Sven:你说得对,我阅读文档太快了。我编辑了我的答案。 - Falanwe

3
    public static bool IsGenericEnumerable(Type type)
    {
        return type.IsGenericType && 
            type.GetInterfaces().Any(
            ti => (ti == typeof (IEnumerable<>) || ti.Name == "IEnumerable"));
    }

    public static bool IsEnumerable(Type type)
    {
        return IsGenericEnumerable(type) || type.IsArray;
    }

0

对我来说,以下内容无法正常工作,

return property.PropertyType.GetInterface(typeof(ICollection<>).FullName) != null;

以下代码曾经有效,

typeof(ICollection<>).IsAssignableFrom(property.PropertyType.GetGenericTypeDefinition())

这是一种快捷的方式来检查ICollection<IInterface>ICollection<BaseClassInTree>

var property = request as PropertyInfo;

property.PropertyType.IsGenericType && (typeof(ICollection<>).IsAssignableFrom(property.PropertyType.GetGenericTypeDefinition())) && typeof().IsAssignableFrom(property.PropertyType.GenericTypeArguments[0])

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接