从IEnumerable <T>获取类型T


106

有没有一种方法来检索类型TIEnumerable<T>通过反射?

例如

我有一个可变的IEnumerable<Child>信息;我想通过反射来检索孩子的类型


1
在什么情况下?IEnumerable <T>是什么?它是作为参数发送的对象实例吗?或者是什么?
Mehrdad Afshari

Answers:


142
IEnumerable<T> myEnumerable;
Type type = myEnumerable.GetType().GetGenericArguments()[0]; 

因此,

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

版画System.String

请参阅MSDNType.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]);
}

一些对象实现了多个泛型,IEnumerable因此有必要返回它们的枚举。

编辑:虽然,我不得不说,对于一个类实现IEnumerable<T>多个对象是一个糟糕的主意T


甚至更糟的是编写一个带有yield return的方法,并尝试在使用此方法创建的变量上调用GetType。它会告诉您这不是通用类型的事件。因此,给定类型为IEnumerable <T>的实例变量,基本上没有通用的方法来获取T
Darin Dimitrov,2009年

1
或尝试使用MyClass类:IEnumerable <int> {}。此类没有通用接口。
Stefan Steinegger,2009年

1
为什么有人会求助于通用参数,然后从其索引器中获取类型呢?那只是在带来灾难,特别是当语言支持typeof(T)时,如@amsprich在他/她的答案中建议的那样,也可以将其用于通用类型或已知类型...
Robert Petz 2013年

与linq查询一起使用时,此操作将失败,WhereSelectEnumerableIterator的第一个通用参数不是。您将获得基础对象的通用参数,而不是接口本身。
Pxtl

myEnumerable.GetType()。GetGenericArguments()[0]为您提供FullName属性,该属性告诉您namespace.classname。如果仅查找类名,请使用myEnumerable.GetType()。GetGenericArguments()[0] .Name
user5534263 2016年

38

我只是做一个扩展方法。这与我投入的所有工作都有效。

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

6
如果您的编译时参考只是object类型,那将不起作用。
Stijn Van Antwerpen

27

我有一个类似的问题。所选答案适用于实际实例。就我而言,我只有一个类型(来自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;
}

拯救了我的一天。就我而言,我添加了一个单独的if语句来处理字符串,因为它实现了IEnumerable <char>
Edmund P Charumbira

Type.GenericTypeArguments-仅适用于dotNet FrameWork版本> = 4.5。否则,请Type.GetGenericArguments改用。
КоеКто

20

如果您知道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);

3
一些对象实现了多个通用的IEnumerable。
杰森

5
@Jason-在那些情况下,“找到T”问题已经是一个可疑的问题;我对此无能为力...
马克·格雷韦尔

任何尝试将其与Type type参数而不是object obj参数一起使用的人,都有一个小陷阱:您不能仅替换为obj.GetType()type因为如果您传入,typeof(IEnumerable<T>)那么您将一无所获。为了解决这个问题,请测试其type自身以查看它是否是泛型,IEnumerable<>然后是其接口。
伊恩·默瑟

8

非常感谢您的讨论。我将其用作以下解决方案的基础,该解决方案对我感兴趣的所有情况(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()
Mass Dot Net

2

只需使用 typeof(T)

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


你并不总是有T.
贾森-

是的,在这种情况下,您可以使用.GetType()。我将修改答案。
发挥

2

一种更简单的情况的替代方案,它要么是IEnumerable<T>或者代替T使用。GenericTypeArgumentsGetGenericArguments()

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

    return genericType;
} else {
    return inputType;
}

1

这是对Eli Algranti解决方案的改进,因为它也可以在IEnumerable<>继承树中任何级别的类型下使用。

此解决方案将从any中获取元素类型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;
    }
}

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

1
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<IActionResult>>>>>>>>

应该返回 IActionResult



0

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

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

0

这是我不可读的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);
}

注意,该方法还考虑了非泛型IEnumerableobject在这种情况下它会返回,因为它采用了一个Type而不是具体的实例作为参数。顺便说一句,因为x代表未知,所以我发现这部影片很有趣,尽管无关紧要..

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.