在 C# 中测试对象是不是属于泛型类型

Posted

技术标签:

【中文标题】在 C# 中测试对象是不是属于泛型类型【英文标题】: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<>);

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

【问题讨论】:

【参考方案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>>

【讨论】:

以上是关于在 C# 中测试对象是不是属于泛型类型的主要内容,如果未能解决你的问题,请参考以下文章

C#关于反射创建泛型类

为啥 C# (4.0) 不允许泛型类类型中的协变和逆变?

c# 自定义的一个泛型类可以序列化吗?

受保护的泛型类 - 是不是支持?

c# 泛型学习

c#基础泛型