getting type T from IEnumerable<T>

C#GenericsReflection

C# Problem Overview


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

C# Solutions


Solution 1 - C#

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.

Solution 2 - C#

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

Solution 3 - C#

I had a similar problem. The selected answer works for actual instances. In my case I had only a type (from a PropertyInfo).

The selected answer fails when the type itself is typeof(IEnumerable<T>) not an implementation of IEnumerable<T>.

For this case the following works:

public static Type GetAnyElementType(Type type)
{
   // Type is Array
   // short-circuit if you expect lots of arrays 
   if (type.IsArray)
      return type.GetElementType();

   // type is IEnumerable<T>;
   if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof (IEnumerable<>))
      return type.GetGenericArguments()[0];

   // type implements/extends IEnumerable<T>;
   var enumType = type.GetInterfaces()
                           .Where(t => t.IsGenericType && 
                                  t.GetGenericTypeDefinition() == typeof(IEnumerable<>))
                           .Select(t => t.GenericTypeArguments[0]).FirstOrDefault();
   return enumType ?? type;
}

Solution 4 - C#

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

Solution 5 - C#

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

Solution 6 - C#

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

Solution 7 - C#

Just use typeof(T)

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

Solution 8 - C#

An alternative for simpler situations where it's either going to be an IEnumerable<T> or T - note use of GenericTypeArguments instead of GetGenericArguments().

Type inputType = o.GetType();
Type genericType;
if ((inputType.Name.StartsWith("IEnumerable"))
    && ((genericType = inputType.GenericTypeArguments.FirstOrDefault()) != null)) {

    return genericType;
} else {
    return inputType;
}

Solution 9 - C#

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

Solution 10 - C#

This is an improvement on Eli Algranti's solution in that it will also work where the IEnumerable<> type is at any level in the inheritance tree.

This solution will obtain the element type from any Type. If the type is not an IEnumerable<>, it will return the type passed in. For objects, use GetType. For types, use typeof, then call this extension method on the result.

public static Type GetGenericElementType(this Type type)
{
    // Short-circuit for Array types
    if (typeof(Array).IsAssignableFrom(type))
    {
        return type.GetElementType();
    }

    while (true)
    {
        // Type is IEnumerable<T>
        if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
        {
            return type.GetGenericArguments().First();
        }

        // Type implements/extends IEnumerable<T>
        Type elementType = (from subType in type.GetInterfaces()
            let retType = subType.GetGenericElementType()
            where retType != subType
            select retType).FirstOrDefault();

        if (elementType != null)
        {
            return elementType;
        }

        if (type.BaseType == null)
        {
            return type;
        }

        type = type.BaseType;
    }
}

Solution 11 - C#

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

Solution 12 - C#

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];
    }

Solution 13 - C#

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 ..

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionUsman MasoodView Question on Stackoverflow
Solution 1 - C#jasonView Answer on Stackoverflow
Solution 2 - C#amsprichView Answer on Stackoverflow
Solution 3 - C#Eli AlgrantiView Answer on Stackoverflow
Solution 4 - C#Marc GravellView Answer on Stackoverflow
Solution 5 - C#BernardoView Answer on Stackoverflow
Solution 6 - C#Tyler HuskinsView Answer on Stackoverflow
Solution 7 - C#reinView Answer on Stackoverflow
Solution 8 - C#Rob ChurchView Answer on Stackoverflow
Solution 9 - C#dahallView Answer on Stackoverflow
Solution 10 - C#NeoView Answer on Stackoverflow
Solution 11 - C#Daniel BrücknerView Answer on Stackoverflow
Solution 12 - C#H7OView Answer on Stackoverflow
Solution 13 - C#Ken KinView Answer on Stackoverflow