42

How can I determine if a property is a kind of array.

Example:

public bool IsPropertyAnArray(PropertyInfo property)
{
    // return true if type is IList<T>, IEnumerable<T>, ObservableCollection<T>, etc...
}
Melursus
  • 10,328
  • 19
  • 69
  • 103
  • 1
    So you mean, if the property is a collection? Things that match your comment are not arrays. – Ben Voigt Feb 24 '12 at 17:04
  • 2
    You probably meant `IEnumerable` instead of `IEnumerator`? – vgru Feb 24 '12 at 17:05
  • @Ben: Well, arrays match his criteria (IList and IEnumerable), but not all things that match his criteria are arrays. :) – Sven Feb 24 '12 at 17:22

5 Answers5

77

You appear to be asking two different questions: whether a type is an array (e.g. string[]) or any collection type.

For the former, simply check property.PropertyType.IsArray.

For the latter, you have to decide what is the minimum criteria you want a type to conform to. For example, you could check for the non-generic IEnumerable by using typeof(IEnumerable).IsAssignableFrom(property.PropertyType). You can also use this for generic interfaces if you know the actual type of T, e.g. typeof(IEnumerable<int>).IsAssignableFrom(property.PropertyType).

Checking for the generic IEnumerable<T> or any other generic interface without knowing the value of T can be done by checking if property.PropertyType.GetInterface(typeof(IEnumerable<>).FullName) is not null. Note that I didn't specify any type for T in that code. You can do the same for IList<T> or any other type you're interested in.

For example you could use the following if you want to check for the generic IEnumerable<T>:

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

Arrays also implement IEnumerable, so they will also return true from that method.

Sven
  • 21,903
  • 4
  • 56
  • 63
  • 1
    This implementation would miss "old school collections", inherited from .Net 1 and which implements IEnumerable but not the generic version IEnumerable. They are less and less used, but some API expects them, and they are really a pain. – Falanwe Feb 24 '12 at 17:15
  • 1
    @Falanwe: Then use the first method I mentioned, which does check for the non-generic IEnumerable. It really depends on his requirements which is best. – Sven Feb 24 '12 at 17:19
  • 1
    I try your method with an property that is a string and the method return true... Do you know why? – Melursus Feb 24 '12 at 18:13
  • 6
    @Melursus: the `String` class implements `IEnumerable`, so it qualifies as a collection under these criteria. You either need to filter it out specifically, or check for a more restrictive interface (e.g. `ICollection` or `IList`). – Sven Feb 25 '12 at 04:15
  • 2
    Any type that implements generic `IEnumerable<>` will also be a non-generic `IEnumerable`, of course. – Jeppe Stig Nielsen Oct 22 '13 at 20:02
  • Please note that type string is regarded as IEnumerable which means it will be included in this check. If you don't want that you can just extend the criteria slightly: property.PropertyType.GetInterface(typeof(IEnumerable<>).FullName) != null && property.PropertyType != typeof(string) – Jakob Lithner May 05 '15 at 19:00
  • This is a concise solution if your coding against the full .NET Framework. But note that [`Type.GetInterface`](https://msdn.microsoft.com/en-us/library/ayfa0fcd.aspx) isn't available in PCLs, etc.. So you may need to use [this solution](http://stackoverflow.com/a/1075059). – HappyNomad Aug 19 '16 at 20:02
22

Excluding String class as it qualifies as a collection because it implements IEnumerable<char>.

public bool IsPropertyACollection(this PropertyInfo property)
{
    return (!typeof(String).Equals(property.PropertyType) && 
        typeof(IEnumerable).IsAssignableFrom(property.PropertyType));
}
Furqan Safdar
  • 16,260
  • 13
  • 59
  • 93
18

If you want to know if the property is an array, it's actually very easy:

property.PropertyType.IsArray;

edit

If you want to know if it's a type that implements IEnumerable, as do all "collection types", it's not very complicated either:

return property.PropertyType.GetInterface("IEnumerable") != null;
Falanwe
  • 4,636
  • 22
  • 37
  • I am not sure if that's what OP really wanted (but I admit naming is a bit misleading). Custom type implementing `IList` will return false from this property. – vgru Feb 24 '12 at 17:04
  • indeed, I misread the question initially. I edited my answer to include any IEnumerable – Falanwe Feb 24 '12 at 17:09
  • 2
    @Falanwe: `IsSubclassOf` doesn't work for interfaces, only base classes. – Sven Feb 24 '12 at 17:13
  • @Falanwe: I can't read French, but the English version says "The IsSubclassOf method **cannot** be used to determine whether an interface derives from another interface, or whether a class implements an interface" (emphases mine). – Sven Feb 24 '12 at 17:17
  • @Sven: you're right, I read the documentation too quickly. I edited my answer – Falanwe Feb 24 '12 at 17:18
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;
    }
gabnaim
  • 1,103
  • 9
  • 13
0

For me the following is not working,

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

Following was working,

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

This is shortcut way to check ICollection<IInterface>orICollection<BaseClassInTree>

var property = request as PropertyInfo;

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