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
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
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);
}
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;
}
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);
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;
}
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
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;
}
Just use typeof(T)
EDIT: Or use .GetType().GetGenericParameter() on an instantiated object if you don't have T.
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;
}
}
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];
}
typeof(IEnumerable<Foo>)
.GetGenericArguments()
[0]
will return the first generic argument - in this case typeof(Foo)
.
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 ..