从 IEnumable < T > 获取类型 T

有没有办法通过反射从 IEnumerable<T>中检索 T类型?

例如:。

我有一个变量 IEnumerable<Child>信息; 我想通过反射检索查尔德的类型

77355 次浏览
IEnumerable<T> myEnumerable;
Type type = myEnumerable.GetType().GetGenericArguments()[0];

因此,

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

指纹 System.String

请参阅 MSDN了解 Type.GetGenericArguments

编辑: 我相信这将解决评论中的问题:

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

GetGenericArguments()[0]将返回第一个泛型参数——在本例中为 typeof(Foo)

Just use typeof(T)

编辑: 或者,如果没有 T,则在实例化对象上使用.GetType () . GetGenericParameter ()。

如果你知道 IEnumerable<T>(通过泛型) ,那么只有 typeof(T)应该工作。否则(对于 object或非通用 IEnumerable) ,检查实现的接口:

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

非常感谢你们的讨论。我使用它作为下面解决方案的基础,它适用于我感兴趣的所有情况(IEnumable、派生类等)。我想我应该在这里分享,以防有人也需要它:

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

我只需要创建一个扩展方法,这个方法可以适用于我所投入的一切。

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

对于更简单的情况,可以选择使用 IEnumerable<T>T-注意使用 GenericTypeArguments而不是 GetGenericArguments()

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


return genericType;
} else {
return inputType;
}

我也遇到过类似的问题,所选择的答案适用于实际情况。 在我的例子中,我只有一个类型(来自 PropertyInfo)。

当类型本身是 typeof(IEnumerable<T>)而不是 IEnumerable<T>的实现时,选定的答案将失败。

在这种情况下,以下措施有效:

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

这是 Eli Algranti 解决方案的一个改进,因为它也可以工作在 IEnumerable<>类型位于继承树中任何级别的地方。

此解决方案将从任何 Type中获取元素类型。如果类型不是 IEnumerable<>,它将返回传入的类型。对于对象,使用 GetType。对于类型,使用 typeof,然后在结果上调用此扩展方法。

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

我通常是这样做的(通过扩展方法) :

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

我知道这有点老,但我相信这种方法将涵盖所有的问题和挑战在评论中陈述。感谢 Eli Algranti 激励了我的工作。

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

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

注意,该方法还考虑到了非泛型 IEnumerable,在本例中它返回 object,因为它使用 Type而不是具体实例作为参数。顺便说一下,对于 X 表示未知数,我发现 这个视频很有趣,尽管它与本文无关。.

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.

我用这种类型测试了这种方法: ICollection<IEnumerable<ICollection<ICollection<IEnumerable<IList<ICollection<IEnumerable<T>>>>>>>>

返回 T