views:

1043

answers:

5

hi, is there a way to retrieve type T from IEnumerable<T> through reflection?

e.g.

i have a variable IEnumerable<Child> info; i want to retrieve Child's type through reflection

+12  A: 
IEnumerable<T> myEnumerable;
Type type = myEnumerable.GetType().GetGenericArguments()[0];

Thusly,

IEnumerable<string> strings = new List<string>();
Console.WriteLine(strings.GetType().GetGenericArguments()[0]);

prints System.String.

See MSDN for Type.GetGenericArguments.

Edit: I believe this will address the concerns in the comments:

// returns an enumeration of T where o : IEnumerable<T>
public IEnumerable<Type> GetGenericIEnumerables(object o) {
    return o.GetType()
            .GetInterfaces()
            .Where(t => t.IsGenericType == true
                && t.GetGenericTypeDefinition() == typeof(IEnumerable<>))
            .Select(t => t.GetGenericArguments()[0]);
}

Some objects implement more than one generic IEnumerable so it is necessary to return an enumeration of them.

Jason
This won't work: Try it with myEnumerable = new string[0].
Darin Dimitrov
Or even worse write a method with yield returns and try to call GetType on a variable created with this method. It will tell you that it is not event a generic type. So basically there is no universal way to get T given an instance variable of type IEnumerable<T>
Darin Dimitrov
Or try with class MyClass : IEnumerable<int> {}. This class doesn't have a generic interface.
Stefan Steinegger
A: 

typeof(IEnumerable<Foo>).GetGenericArguments()[0] will return the first generic argument - in this case typeof(Foo).

Daniel Brückner
+2  A: 

Just use typeof(T)

EDIT: Or use .GetType().GetGenericParameter() on an instantiated object if you don't have T.

rein
You don't always have T.
Jason
True, in which case you can use .GetType(). I'll modify my answer.
rein
As other commenters noticed, this doesn't always work.
Jason
+4  A: 

If you know the IEnumerable<T> (via generics), then just typeof(T) should work. Otherwise (for object, or the non-generic IEnumerable), check the interfaces implemented:

        object obj = new string[] { "abc", "def" };
        Type type = null;
        foreach (Type iType in obj.GetType().GetInterfaces())
        {
            if (iType.IsGenericType && iType.GetGenericTypeDefinition()
                == typeof(IEnumerable<>))
            {
                type = iType.GetGenericArguments()[0];
                break;
            }
        }
        if (type != null) Console.WriteLine(type);
Marc Gravell
Some objects implement more than one generic IEnumerable.
Jason
@Jason - and in those cases, the question of "find the T" is already a dubious question; I can't do anything about that...
Marc Gravell
A: 

Thank you very much for the discussion. I used it as a basis for the solution below, which works well for all cases that are of interest to me (IEnumerable, derived classes, etc). Thought I should share here in case anyone needs it also:

  Type GetItemType(object someCollection)
  {
    var type = someCollection.GetType();
    var ienum = type.GetInterface(typeof(IEnumerable<>).Name);
    return ienum != null
      ? ienum.GetGenericArguments()[0]
      : null;
  }
Bernardo