getting type T from IEnumerable<T>

Viewed 75694

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

13 Answers
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
                && 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.

Edit: Although, I have to say, it's a terrible idea for a class to implement IEnumerable<T> for more than one T.

I'd just make an extension method. This worked with everything I threw at it.

public static Type GetItemType<T>(this IEnumerable<T> enumerable)
{
    return typeof(T);
}

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);

I know this is a bit old, but I believe this method will cover all the problems and challenges stated in the comments. Credit to Eli Algranti for inspiring my work.

/// <summary>Finds the type of the element of a type. Returns null if this type does not enumerate.</summary>
/// <param name="type">The type to check.</param>
/// <returns>The element type, if found; otherwise, <see langword="null"/>.</returns>
public static Type FindElementType(this Type type)
{
   if (type.IsArray)
      return type.GetElementType();

   // type is IEnumerable<T>;
   if (ImplIEnumT(type))
      return type.GetGenericArguments().First();

   // type implements/extends IEnumerable<T>;
   var enumType = type.GetInterfaces().Where(ImplIEnumT).Select(t => t.GetGenericArguments().First()).FirstOrDefault();
   if (enumType != null)
      return enumType;

   // type is IEnumerable
   if (IsIEnum(type) || type.GetInterfaces().Any(IsIEnum))
      return typeof(object);

   return null;

   bool IsIEnum(Type t) => t == typeof(System.Collections.IEnumerable);
   bool ImplIEnumT(Type t) => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>);
}
public static Type GetInnerGenericType(this Type type)
{
  // Attempt to get the inner generic type
  Type innerType = type.GetGenericArguments().FirstOrDefault();

  // Recursively call this function until no inner type is found
  return innerType is null ? type : innerType.GetInnerGenericType();
}

This is a recursive function that will go depth first down the list of generic types until it gets a concrete type definition with no inner generic types.

I tested this method with this type: ICollection<IEnumerable<ICollection<ICollection<IEnumerable<IList<ICollection<IEnumerable<T>>>>>>>>

which should return T

Just use typeof(T)

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

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

this is how I usually do it (via extension method):

public static Type GetIEnumerableUnderlyingType<T>(this T iEnumerable)
    {
        return typeof(T).GetTypeInfo().GetGenericArguments()[(typeof(T)).GetTypeInfo().GetGenericArguments().Length - 1];
    }

Here's my unreadable Linq query expression version ..

public static Type GetEnumerableType(this Type t) {
    return !typeof(IEnumerable).IsAssignableFrom(t) ? null : (
    from it in (new[] { t }).Concat(t.GetInterfaces())
    where it.IsGenericType
    where typeof(IEnumerable<>)==it.GetGenericTypeDefinition()
    from x in it.GetGenericArguments() // x represents the unknown
    let b = it.IsConstructedGenericType // b stand for boolean
    select b ? x : x.BaseType).FirstOrDefault()??typeof(object);
}

Note the method also takes non-generic IEnumerable into account, it returns object in this case, because it takes a Type rather than a concrete instance as the argument. By the way, for x represents the unknown, I found this video insteresting, though it is irrelevant ..

Related