【问题标题】:getting type T from IEnumerable<T>从 IEnumerable<T> 获取类型 T
【发布时间】:2009-05-25 12:12:43
【问题描述】:

有没有办法通过反射从IEnumerable&lt;T&gt; 检索类型T

例如

我有一个变量IEnumerable&lt;Child&gt; 信息;我想通过反射检索 Child 的类型

【问题讨论】:

  • 在什么情况下?这是什么 IEnumerable?它是作为参数发送的对象实例吗?还是什么?

标签: c# generics reflection


【解决方案1】:
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&lt;T&gt; 是一个糟糕的主意。

【讨论】:

  • 或者更糟糕的是,编写一个带有收益返回的方法,并尝试在使用此方法创建的变量上调用 GetType。它会告诉你它不是泛型类型的事件。所以基本上没有通用的方法来获取 T 给定类型 IEnumerable 的实例变量
  • 或尝试使用 class MyClass : IEnumerable {}。此类没有通用接口。
  • 为什么有人会求助于获取泛型参数,然后从其索引器中获取类型?那只是自找麻烦,尤其是当语言支持 typeof(T) 时,就像@amsprich 在他/她的回答中所建议的那样,它也可以与通用类型或已知类型一起使用......
  • 当您使用数组时失败,该数组确实转换为 IEnumerable
  • 对于数组,你可以GetType().GetElementType()
【解决方案2】:

我只是做一个扩展方法。这适用于我扔给它的所有东西。

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

【讨论】:

  • 如果您的编译时引用只是对象类型,它将不起作用。
【解决方案3】:

我遇到了类似的问题。所选答案适用于实际情况。 就我而言,我只有一个类型(来自PropertyInfo)。

当类型本身是 typeof(IEnumerable&lt;T&gt;) 而不是 IEnumerable&lt;T&gt; 的实现时,选择的答案会失败。​​

对于这种情况,以下工作:

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
  • Type.GenericTypeArguments - 仅适用于 dotNet FrameWork 版本 >= 4.5。否则 - 改用Type.GetGenericArguments
【解决方案4】:

如果您知道IEnumerable&lt;T&gt;(通过泛型),那么只需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);

【讨论】:

  • 有些对象实现了多个通用 IEnumerable。
  • @Jason - 在那些情况下,“找到 T”的问题已经是一个可疑的问题;我对此无能为力...
  • 对于任何试图将其与Type type 参数而不是object obj 参数一起使用的人的一个小问题:你不能只用type 替换obj.GetType(),因为如果你传入@ 987654330@你什么也得不到。为了解决这个问题,测试type 本身,看看它是否是IEnumerable&lt;&gt; 的泛型,然后是它的接口。
【解决方案5】:

非常感谢您的讨论。我将它用作以下解决方案的基础,它适用于我感兴趣的所有情况(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&lt;&gt;).Name)?.GetGenericArguments()?.FirstOrDefault()
【解决方案6】:
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&lt;IEnumerable&lt;ICollection&lt;ICollection&lt;IEnumerable&lt;IList&lt;ICollection&lt;IEnumerable&lt;T&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;

应该返回T

【讨论】:

    【解决方案7】:

    只需使用typeof(T)

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

    【讨论】:

    • True,在这种情况下,您可以使用 .GetType()。我会修改我的答案。
    【解决方案8】:

    对于更简单的情况,可以使用IEnumerable&lt;T&gt;T - 请注意使用GenericTypeArguments 而不是GetGenericArguments()

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

    【讨论】:

      【解决方案9】:

      我知道这有点陈旧,但我相信这种方法将涵盖 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<>);
      }
      

      【讨论】:

        【解决方案10】:

        这是对 Eli Algranti 解决方案的改进,因为它也适用于 IEnumerable&lt;&gt; 类型位于继承树中的任何级别的情况。

        此解决方案将从任何Type 获取元素类型。如果类型不是IEnumerable&lt;&gt;,则返回传入的类型。对于对象,使用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;
            }
        }
        

        【讨论】:

          【解决方案11】:

          typeof(IEnumerable&lt;Foo&gt;).GetGenericArguments()[0] 将返回第一个通用参数 - 在本例中为 typeof(Foo)

          【讨论】:

            【解决方案12】:

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

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

            【讨论】:

              【解决方案13】:

              这是我不可读的 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 很有趣,虽然这无关紧要..

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多