【发布时间】:2009-05-25 12:12:43
【问题描述】:
有没有办法通过反射从IEnumerable<T> 检索类型T?
例如
我有一个变量IEnumerable<Child> 信息;我想通过反射检索 Child 的类型
【问题讨论】:
-
在什么情况下?这是什么 IEnumerable
?它是作为参数发送的对象实例吗?还是什么?
标签: c# generics reflection
有没有办法通过反射从IEnumerable<T> 检索类型T?
例如
我有一个变量IEnumerable<Child> 信息;我想通过反射检索 Child 的类型
【问题讨论】:
标签: c# generics reflection
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。
编辑:我相信这将解决 cmets 中的问题:
// 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]);
}
有些对象实现了多个通用IEnumerable,因此有必要返回它们的枚举。
编辑:虽然,我不得不说,一个类为多个T 实现IEnumerable<T> 是一个糟糕的主意。
【讨论】:
我只是做一个扩展方法。这适用于我扔给它的所有东西。
public static Type GetItemType<T>(this IEnumerable<T> enumerable)
{
return typeof(T);
}
【讨论】:
我遇到了类似的问题。所选答案适用于实际情况。
就我而言,我只有一个类型(来自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;
}
【讨论】:
Type.GenericTypeArguments - 仅适用于 dotNet FrameWork 版本 >= 4.5。否则 - 改用Type.GetGenericArguments。
如果您知道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);
【讨论】:
Type type 参数而不是object obj 参数一起使用的人的一个小问题:你不能只用type 替换obj.GetType(),因为如果你传入@ 987654330@你什么也得不到。为了解决这个问题,测试type 本身,看看它是否是IEnumerable<> 的泛型,然后是它的接口。
非常感谢您的讨论。我将它用作以下解决方案的基础,它适用于我感兴趣的所有情况(IEnumerable、派生类等)。以为我应该在这里分享以防有人也需要它:
Type GetItemType(object someCollection)
{
var type = someCollection.GetType();
var ienum = type.GetInterface(typeof(IEnumerable<>).Name);
return ienum != null
? ienum.GetGenericArguments()[0]
: null;
}
【讨论】:
someCollection.GetType().GetInterface(typeof(IEnumerable<>).Name)?.GetGenericArguments()?.FirstOrDefault()
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();
}
这是一个递归函数,它将首先深入泛型类型列表,直到它得到一个没有内部泛型类型的具体类型定义。
我用这种类型测试了这个方法:
ICollection<IEnumerable<ICollection<ICollection<IEnumerable<IList<ICollection<IEnumerable<T>>>>>>>>
应该返回T
【讨论】:
只需使用typeof(T)
编辑: 或者,如果您没有 T,请在实例化对象上使用 .GetType().GetGenericParameter()。
【讨论】:
对于更简单的情况,可以使用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;
}
【讨论】:
我知道这有点陈旧,但我相信这种方法将涵盖 cmets 中所述的所有问题和挑战。感谢 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<>);
}
【讨论】:
这是对 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;
}
}
【讨论】:
typeof(IEnumerable<Foo>).GetGenericArguments()[0] 将返回第一个通用参数 - 在本例中为 typeof(Foo)。
【讨论】:
这是我通常的做法(通过扩展方法):
public static Type GetIEnumerableUnderlyingType<T>(this T iEnumerable)
{
return typeof(T).GetTypeInfo().GetGenericArguments()[(typeof(T)).GetTypeInfo().GetGenericArguments().Length - 1];
}
【讨论】:
这是我不可读的 Linq 查询表达式版本..
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 代表未知数,我发现 this video 很有趣,虽然这无关紧要..
【讨论】: