【问题标题】:How to get the type of T from a member of a generic class or method如何从泛型类或方法的成员中获取 T 的类型
【发布时间】:2010-10-08 03:00:42
【问题描述】:

假设我在一个类或方法中有一个泛型成员,如下所示:

public class Foo<T>
{
    public List<T> Bar { get; set; }
    
    public void Baz()
    {
        // get type of T
    }   
}

当我实例化该类时,T 变为 MyTypeObject1,因此该类具有通用列表属性:List&lt;MyTypeObject1&gt;。这同样适用于非泛型类中的泛型方法:

public class Foo
{
    public void Bar<T>()
    {
        var baz = new List<T>();
        
        // get type of T
    }
}

我想知道我的班级列表包含什么类型的对象。那么名为Bar 或局部变量baz 的列表属性包含什么类型的T

我不能Bar[0].GetType(),因为列表可能包含零个元素。我该怎么做?

【问题讨论】:

    标签: c# .net generics


    【解决方案1】:

    如果我理解正确,您的列表具有与容器类本身相同的类型参数。如果是这样的话,那么:

    Type typeParameterType = typeof(T);
    

    如果您有幸将object 作为类型参数,请参阅Marc's answer

    【讨论】:

    • 我喜欢typeof 的可读性。如果您想知道 T 的类型,只需使用typeof(T) :)
    • 我实际上只是使用了 typeof(Type) 并且效果很好。
    • 不过,您不能将 typeof() 与泛型参数一起使用。
    • @Reynevan 当然,您可以将typeof() 与通用参数一起使用。你有任何例子说明它不起作用吗?还是你混淆了类型参数和引用?
    【解决方案2】:

    (注意:我假设您只知道objectIList 或类似的,并且该列表在运行时可以是任何类型)

    如果你知道它是List&lt;T&gt;,那么:

    Type type = abc.GetType().GetGenericArguments()[0];
    

    另一种选择是查看索引器:

    Type type = abc.GetType().GetProperty("Item").PropertyType;
    

    使用新的 TypeInfo:

    using System.Reflection;
    // ...
    var type = abc.GetType().GetTypeInfo().GenericTypeArguments[0];
    

    【讨论】:

    • 类型类型 = abc.GetType().GetGenericArguments()[0]; ==> 超出范围的数组索引...
    • @Daok :那么它不是一个列表
    • 需要 BindingList 或 List 或任何包含 的对象。我在做什么使用自定义 BindingListView
    • 试一试 BindingList,我们的 BindingListView 继承自 BindingList 并且我都尝试了您的两个选项,但它不起作用。我可能做错了什么...但我认为此解决方案适用于 List 类型,但不适用于其他类型的列表。
    • 类型类型 = abc.GetType().GetProperty("Item").PropertyType;返回 BindingListView 而不是 MyObject...
    【解决方案3】:

    使用以下扩展方法,您可以不用反思:

    public static Type GetListType<T>(this List<T> _)
    {
        return typeof(T);
    }
    

    或更笼统地说:

    public static Type GetEnumeratedType<T>(this IEnumerable<T> _)
    {
        return typeof(T);
    }
    

    用法:

    List<string>        list    = new List<string> { "a", "b", "c" };
    IEnumerable<string> strings = list;
    IEnumerable<object> objects = list;
    
    Type listType    = list.GetListType();           // string
    Type stringsType = strings.GetEnumeratedType();  // string
    Type objectsType = objects.GetEnumeratedType();  // BEWARE: object
    

    【讨论】:

    • 这只有在编译时已经知道T 的类型时才有用。在这种情况下,您根本不需要任何代码。
    • @recursive:如果您使用的是匿名类型的列表,这很有用。
    【解决方案4】:

    试试

    list.GetType().GetGenericArguments()
    

    【讨论】:

    • new List().GetType().GetGenericArguments() 在此处返回 System.Type[1],其中 System.Int32 作为条目
    • @Rauhotz 的GetGenericArguments 方法返回一个Type 的Array 对象,然后你需要解析出你需要的Generic Type 的位置。比如Type&lt;TKey, TValue&gt;:你需要GetGenericArguments()[0]来获取TKey类型和GetGenericArguments()[1]来获取TValue类型
    【解决方案5】:

    以下内容对我有用。其中 myList 是某种未知类型的列表。

    IEnumerable myEnum = myList as IEnumerable;
    Type entryType = myEnum.AsQueryable().ElementType;
    

    【讨论】:

    • 我得到一个错误,它需要一个类型参数(即&lt;T&gt;
    • Joseph 和其他人,以摆脱 System.Collections 中的错误。
    • 我只需要第二行。 List 已经是 IEnumerable 的实现,所以演员表似乎没有添加任何东西。不过谢谢,这是一个很好的解决方案。
    • 这对我有用,因为我有一个 ref 类型,它可以而且经常没有项目 - 其他答案不起作用
    【解决方案6】:

    如果您不需要整个 Type 变量而只想检查类型,您可以轻松创建一个临时变量并使用 is 运算符。

    T checkType = default(T);
    
    if (checkType is MyClass)
    {}
    

    【讨论】:

    • 这应该是公认的答案,当然是最好的。
    • 像专业人士一样编写代码 :)
    • @EbrahimKarimi 当然:-)
    • 我猜你从来没有尝试过snipboard.io/SuGDIA.jpg
    • @VõQuangHòa 我完全赞成您的评论。你的 cmets 在这里是正确的答案
    【解决方案7】:

    您可以将这个用于泛型列表的返回类型:

    public string ListType<T>(T value)
    {
        var valueType = value.GetType().GenericTypeArguments[0].FullName;
        return valueType;
    }
    

    【讨论】:

      【解决方案8】:

      我使用这个扩展方法来完成类似的事情:

      public static string GetFriendlyTypeName(this Type t)
      {
          var typeName = t.Name.StripStartingWith("`");
          var genericArgs = t.GetGenericArguments();
          if (genericArgs.Length > 0)
          {
              typeName += "<";
              foreach (var genericArg in genericArgs)
              {
                  typeName += genericArg.GetFriendlyTypeName() + ", ";
              }
              typeName = typeName.TrimEnd(',', ' ') + ">";
          }
          return typeName;
      }
      
      public static string StripStartingWith(this string s, string stripAfter)
      {
          if (s == null)
          {
              return null;
          }
          var indexOf = s.IndexOf(stripAfter, StringComparison.Ordinal);
          if (indexOf > -1)
          {
              return s.Substring(0, indexOf);
          }
          return s;
      }
      

      你可以这样使用它:

      [TestMethod]
      public void GetFriendlyTypeName_ShouldHandleReallyComplexTypes()
      {
          typeof(Dictionary<string, Dictionary<string, object>>).GetFriendlyTypeName()
              .ShouldEqual("Dictionary<String, Dictionary<String, Object>>");
      }
      

      这不是您想要的,但它有助于展示所涉及的技术。

      【讨论】:

      • 解释一下。例如,它的要点是什么?想法是什么?请通过编辑您的答案来回复,而不是在 cmets 中(without "Edit:"、"Update:" 或类似的 - 答案应该看起来好像是今天写的)。
      【解决方案9】:

      考虑一下:

      我用它以同样的方式导出20个类型列表:

      private void Generate<T>()
      {
          T item = (T)Activator.CreateInstance(typeof(T));
      
          ((T)item as DemomigrItemList).Initialize();
      
          Type type = ((T)item as DemomigrItemList).AsEnumerable().FirstOrDefault().GetType();
          if (type == null) 
              return;
          if (type != typeof(account)) // Account is listitem in List<account>
          {
              ((T)item as DemomigrItemList).CreateCSV(type);
          }
      }
      

      【讨论】:

      • 如果 T 是实际添加的对象的抽象超类,这不起作用。更不用说,只是new T(); 会和(T)Activator.CreateInstance(typeof(T)); 做同样的事情。它确实需要您将where T : new() 添加到类/函数定义中,但是如果您想要创建对象,无论如何都应该这样做。
      • 另外,您在 FirstOrDefault 条目上调用 GetType 会导致潜在的空引用异常。如果您确定它会返回至少一项,为什么不改用First
      【解决方案10】:

      GetGenericArgument() 方法必须在您的实例的基本类型上设置(其类是泛型类myClass&lt;T&gt;)。否则,它返回一个类型[0]。

      例子:

      Myclass<T> instance = new Myclass<T>();
      Type[] listTypes = typeof(instance).BaseType.GetGenericArguments();
      

      【讨论】:

        【解决方案11】:

        您可以从任何实现 IEnumerable 的集合类型中获取“T”的类型:

        public static Type GetCollectionItemType(Type collectionType)
        {
            var types = collectionType.GetInterfaces()
                .Where(x => x.IsGenericType 
                    && x.GetGenericTypeDefinition() == typeof(IEnumerable<>))
                .ToArray();
            // Only support collections that implement IEnumerable<T> once.
            return types.Length == 1 ? types[0].GetGenericArguments()[0] : null;
        }
        

        请注意,它不支持实现 IEnumerable 两次的集合类型,例如

        public class WierdCustomType : IEnumerable<int>, IEnumerable<string> { ... }
        

        如果你需要支持这个,我想你可以返回一个类型数组...

        此外,如果您经常这样做(例如在循环中),您可能还希望缓存每个集合类型的结果。

        【讨论】:

          【解决方案12】:

          使用3dGrabber's solution

          public static T GetEnumeratedType<T>(this IEnumerable<T> _)
          {
              return default(T);
          }
          
          //and now
          
          var list = new Dictionary<string, int>();
          var stronglyTypedVar = list.GetEnumeratedType();
          

          【讨论】:

            【解决方案13】:
            public bool IsCollection<T>(T value){
              var valueType = value.GetType();
              return valueType.IsArray() || typeof(IEnumerable<object>).IsAssignableFrom(valueType) || typeof(IEnumerable<T>).IsAssignableFrom(valuetype);
            }
            

            【讨论】:

            • 这似乎解决了类型是否是 list-y 类型的问题,但问题更多是关于如何确定已知为 List 的类型的泛型类型参数被初始化。
            • 解释一下。例如,它的要点是什么?想法是什么?请通过editing your answer 回复,而不是在 cmets 中(without "Edit:"、"Update:" 或类似的 - 答案应该看起来像是今天写的)。
            【解决方案14】:

            如果你想知道一个属性的底层类型,试试这个:

            propInfo.PropertyType.UnderlyingSystemType.GenericTypeArguments[0]
            

            【讨论】:

              【解决方案15】:

              我就是这样做的:

              internal static Type GetElementType(this Type type)
              {
                  // Use type.GenericTypeArguments if it exists
                  if (type.GenericTypeArguments.Any())
                      return type.GenericTypeArguments.First();
              
                  return type.GetRuntimeProperty("Item").PropertyType);
              }
              

              然后这样称呼它:

              var item = Activator.CreateInstance(iListType.GetElementType());
              

              或者

              var item = Activator.CreateInstance(Bar.GetType().GetElementType());
              

              【讨论】:

                【解决方案16】:

                试试这个。

                if (typeof(T) == typeof(Person))

                【讨论】:

                  【解决方案17】:

                  类型:

                  type = list.AsEnumerable().SingleOrDefault().GetType();
                  

                  【讨论】:

                  • 如果列表中没有可供测试的元素,这将引发 NullReferenceException。
                  • SingleOrDefault() 在有两个或更多元素时也会抛出 InvalidOperationException
                  • 这个答案是错误的,正如 \@rossisdead 和 \@devgeezer 正确指出的那样。
                  猜你喜欢
                  • 1970-01-01
                  • 2018-11-06
                  • 1970-01-01
                  • 2013-04-17
                  • 1970-01-01
                  • 2021-09-26
                  • 1970-01-01
                  • 1970-01-01
                  • 2013-07-23
                  相关资源
                  最近更新 更多