【问题标题】:Testing if object is of generic type in C#在 C# 中测试对象是否属于泛型类型
【发布时间】:2010-11-02 05:18:12
【问题描述】:

我想测试一个对象是否属于泛型类型。我尝试了以下方法但没有成功:

public bool Test()
{
    List<int> list = new List<int>();
    return list.GetType() == typeof(List<>);
}

我做错了什么以及如何执行此测试?

【问题讨论】:

    标签: c# .net generics reflection types


    【解决方案1】:

    如果要检查它是否是泛型类型的实例:

    return list.GetType().IsGenericType;
    

    如果你想检查它是否是通用的List&lt;T&gt;

    return list.GetType().GetGenericTypeDefinition() == typeof(List<>);
    

    正如 Jon 所指出的,这会检查确切的类型等价性。返回false 并不一定意味着list is List&lt;T&gt; 返回false(即不能将对象分配给List&lt;T&gt; 变量)。

    【讨论】:

    • 虽然不会检测到子类型。看我的回答。接口也更难:(
    • 如果 GetGenericTypeDefinition 不是泛型类型,调用将抛出。请务必先检查。
    • @JonSkeet 您可以使用list.GetType().BaseType 属性检测子类型。
    • @MohammedLarabi:是的,这正是我的答案所做的,递归......
    【解决方案2】:
    return list.GetType().IsGenericType;
    

    【讨论】:

    • 对于不同的问题是正确的。对于这个问题,它是不正确的,因为它只解决了(大大少于)一半的问题。
    • Stan R 的回答实际上回答了所提出的问题,但 OP 的真正意思是“测试对象是否属于 C# 中的 特定 泛型类型”,为此这个答案确实不完整。
    • 人们对我投了反对票,因为我在“是”泛型类型而不是“是”泛型类型的上下文中回答了这个问题。英语是我的第二语言,这种语言的细微差别经常让我忽略,为我辩护的是,OP 并没有特别要求针对特定类型进行测试,并且在标题中询问“属于”通用类型......不知道为什么我应该投反对票一个模棱两可的问题。
    • 现在您知道了,您可以改进您的答案,使其更加具体和正确。
    【解决方案3】:

    我假设您不仅想知道类型是否是泛型,还想知道对象是否是特定泛型类型的实例,而不知道类型参数。

    不幸的是,这并不是非常简单。如果泛型类型是一个类(在这种情况下就是这样),那还不错,但对于接口来说就更难了。这是一个类的代码:

    using System;
    using System.Collections.Generic;
    using System.Reflection;
    
    class Test
    {
        static bool IsInstanceOfGenericType(Type genericType, object instance)
        {
            Type type = instance.GetType();
            while (type != null)
            {
                if (type.IsGenericType &&
                    type.GetGenericTypeDefinition() == genericType)
                {
                    return true;
                }
                type = type.BaseType;
            }
            return false;
        }
    
        static void Main(string[] args)
        {
            // True
            Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
                                                      new List<string>()));
            // False
            Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
                                                      new string[0]));
            // True
            Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
                                                      new SubList()));
            // True
            Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
                                                      new SubList<int>()));
        }
    
        class SubList : List<string>
        {
        }
    
        class SubList<T> : List<T>
        {
        }
    }
    

    编辑:如 cmets 中所述,这可能适用于接口:

    foreach (var i in type.GetInterfaces())
    {
        if (i.IsGenericType && i.GetGenericTypeDefinition() == genericType)
        {
            return true;
        }
    }
    

    我偷偷怀疑这可能存在一些尴尬的边缘情况,但我现在找不到它失败的情况。

    【讨论】:

    • 刚刚发现这个问题。它只沿着单行继承。如果在此过程中,您有一个包含基类 您正在寻找的接口的基础,那么这只会沿着类路径。
    • @Groxx:是的。我刚刚发现我确实在答案中提到了这一点:“如果泛型类型是一个类(在这种情况下就是这样),这还不错,但对于接口来说更难。这是一个类的代码”跨度>
    • 如果你没有办法知道怎么办?比如,它可能是 int 或 string,但你不知道。这似乎会产生误报......所以你没有 T 可以使用,你只是在查看某个对象的属性,其中一个是一个列表。你怎么知道它是一个列表,所以你可以撕掉它?我的意思是,您在任何地方都没有 T 也没有要使用的类型。你可以猜出每种类型(是 List 吗?是 List 吗?)但你想知道的是 IS THIS AA LIST 吗? 这个问题似乎很难回答。
    • @RiverC:是的,你是对的 - 由于各种原因,它相当难以回答。如果你只是在谈论一个类,那还不错……你可以继续沿着继承树走,看看你是否以某种形式点击了List&lt;T&gt;。如果包含接口,那真的很棘手。
    • 您不能用调用IsAssignableFrom 而不是相等运算符(==) 来替换IsInstanceOfGenericType 中的循环吗?
    【解决方案4】:

    您可以使用动态代码来使用较短的代码,尽管这可能比纯反射慢:

    public static class Extension
    {
        public static bool IsGenericList(this object o)
        {
           return IsGeneric((dynamic)o);
        }
    
        public static bool IsGeneric<T>(List<T> o)
        {
           return true;
        }
    
        public static bool IsGeneric( object o)
        {
            return false;
        }
    }
    
    
    
    var l = new List<int>();
    l.IsGenericList().Should().BeTrue();
    
    var o = new object();
    o.IsGenericList().Should().BeFalse();
    

    【讨论】:

      【解决方案5】:

      这是我最喜欢的两种扩展方法,涵盖了泛型类型检查的大多数边缘情况:

      适用于:

      • 多个(通用)接口
      • 多个(通用)基类
      • 有一个重载,如果它返回 true,则会“淘汰”特定的泛型类型(请参阅单元测试的示例):

        public static bool IsOfGenericType(this Type typeToCheck, Type genericType)
        {
            Type concreteType;
            return typeToCheck.IsOfGenericType(genericType, out concreteType); 
        }
        
        public static bool IsOfGenericType(this Type typeToCheck, Type genericType, out Type concreteGenericType)
        {
            while (true)
            {
                concreteGenericType = null;
        
                if (genericType == null)
                    throw new ArgumentNullException(nameof(genericType));
        
                if (!genericType.IsGenericTypeDefinition)
                    throw new ArgumentException("The definition needs to be a GenericTypeDefinition", nameof(genericType));
        
                if (typeToCheck == null || typeToCheck == typeof(object))
                    return false;
        
                if (typeToCheck == genericType)
                {
                    concreteGenericType = typeToCheck;
                    return true;
                }
        
                if ((typeToCheck.IsGenericType ? typeToCheck.GetGenericTypeDefinition() : typeToCheck) == genericType)
                {
                    concreteGenericType = typeToCheck;
                    return true;
                }
        
                if (genericType.IsInterface)
                    foreach (var i in typeToCheck.GetInterfaces())
                        if (i.IsOfGenericType(genericType, out concreteGenericType))
                            return true;
        
                typeToCheck = typeToCheck.BaseType;
            }
        }
        

      这是一个演示(基本)功能的测试:

       [Test]
          public void SimpleGenericInterfaces()
          {
              Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IEnumerable<>)));
              Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IQueryable<>)));
      
              Type concreteType;
              Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IEnumerable<>), out concreteType));
              Assert.AreEqual(typeof(IEnumerable<string>), concreteType);
      
              Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IQueryable<>), out concreteType));
              Assert.AreEqual(typeof(IQueryable<string>), concreteType);
      
      
          }
      

      【讨论】:

        【解决方案6】:
        public static string WhatIsMyType<T>()
        {
            return typeof(T).NameWithGenerics();
        }
        
        public static string NameWithGenerics(this Type type)
        {
            if (type == null)
                throw new ArgumentNullException(nameof(type));
        
            if (type.IsArray)
                return $"{type.GetElementType()?.Name}[]";
        
            if (!type.IsGenericType) 
                return type.Name;
        
            var name = type.GetGenericTypeDefinition().Name;
            var index = name.IndexOf('`');
            var newName = index == -1 ? name : name.Substring(0, index);
                
            var list = type.GetGenericArguments().Select(NameWithGenerics).ToList();
            return $"{newName}<{string.Join(",", list)}>";
        }
        

        现在用这个测试:

        Console.WriteLine(WhatIsMyType<IEnumerable<string>>());
        Console.WriteLine(WhatIsMyType<List<int>>());
        Console.WriteLine(WhatIsMyType<IList<int>>());
        Console.WriteLine(WhatIsMyType<List<ContentBlob>>());
        Console.WriteLine(WhatIsMyType<int[]>());
        Console.WriteLine(WhatIsMyType<ContentBlob>());
        Console.WriteLine(WhatIsMyType<Dictionary<string, Dictionary<int, int>>>());
        

        你会得到

        IEnumerable<String>
        List<Int32>
        IList<Int32>
        List<ContentBlob>
        Int32[]
        ContentBlob
        Dictionary<String,Dictionary<Int32,Int32>>
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-09-12
          • 2012-06-09
          • 1970-01-01
          • 2016-01-14
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多