14

I need to know if the type of a property in a class is a generic collection (List, ObservableCollection) using the PropertyInfo class.

foreach (PropertyInfo p in (o.GetType()).GetProperties())
{
    if(p is Collection<T> ????? )

}
favo
  • 5,426
  • 9
  • 42
  • 61
Lance
  • 2,774
  • 4
  • 37
  • 57

2 Answers2

33
Type tColl = typeof(ICollection<>);
foreach (PropertyInfo p in (o.GetType()).GetProperties()) {
    Type t = p.PropertyType;
    if (t.IsGenericType && tColl.IsAssignableFrom(t.GetGenericTypeDefinition()) ||
        t.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == tColl)) {
        Console.WriteLine(p.Name + " IS an ICollection<>");
    } else {
        Console.WriteLine(p.Name + " is NOT an ICollection<>");
    }
}

You need the tests t.IsGenericType and x.IsGenericType, otherwise GetGenericTypeDefinition() will throw an exception if the type is not generic.

If the property is declared as ICollection<T> then tColl.IsAssignableFrom(t.GetGenericTypeDefinition()) will return true.

If the property is declared as a type which implements ICollection<T> then t.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == tColl) will return true.

Note that tColl.IsAssignableFrom(t.GetGenericTypeDefinition()) returns false for a List<int> for example.


I have tested all these combinations for MyT o = new MyT();

private interface IMyCollInterface1 : ICollection<int> { }
private interface IMyCollInterface2<T> : ICollection<T> { }
private class MyCollType1 : IMyCollInterface1 { ... }
private class MyCollType2 : IMyCollInterface2<int> { ... }
private class MyCollType3<T> : IMyCollInterface2<T> { ... }

private class MyT
{
    public ICollection<int> IntCollection { get; set; }
    public List<int> IntList { get; set; }
    public IMyCollInterface1 iColl1 { get; set; }
    public IMyCollInterface2<int> iColl2 { get; set; }
    public MyCollType1 Coll1 { get; set; }
    public MyCollType2 Coll2 { get; set; }
    public MyCollType3<int> Coll3 { get; set; }
    public string StringProp { get; set; }
}

Output:

IntCollection IS an ICollection<>
IntList IS an ICollection<>
iColl1 IS an ICollection<>
iColl2 IS an ICollection<>
Coll1 IS an ICollection<>
Coll2 IS an ICollection<>
Coll3 IS an ICollection<>
StringProp is NOT an ICollection<>
Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
13

GetGenericTypeDefinition and typeof(Collection<>) will do the job:

if(p.PropertyType.IsGenericType && typeof(Collection<>).IsAssignableFrom(p.PropertyType.GetGenericTypeDefinition())
Manuel Koch
  • 331
  • 2
  • 14
Anton Gogolev
  • 113,561
  • 39
  • 200
  • 288
  • 4
    Shouldn't you test against something like `ICollection` rather than `Collection`? Many of the generic collections (eg, `List`) don't inherit from `Collection`. – LukeH Oct 15 '09 at 08:53
  • 2
    `p.GetType()` will return a `Type` which describes `RuntimePropertyInfo` instead of the type of the property. Also `GetGenericTypeDefinition()` throws an exception for non-generic types. – Olivier Jacot-Descombes Jan 14 '12 at 17:54
  • 1
    Exactly, GetGenericTypeDefinition throws an exception for non-generic types. – Shaggydog Aug 08 '14 at 14:16
  • i am using this `pi[index].PropertyType.IsGenericType && typeof(ICollection<>).IsAssignableFrom(pi[index].PropertyType.GetGenericTypeDefinition())` and worked perfectly for my case – Rafa Oct 01 '18 at 14:44