测试对象在C#中是否为通用类型


134

我想执行一个对象是否为通用类型的测试。我已经尝试了以下方法,但均未成功:

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

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

Answers:


201

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

return list.GetType().IsGenericType;

如果要检查它是否是通用的List<T>

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

正如乔恩(Jon)所指出的那样,这将检查确切的类型等效性。返回false并不一定意味着list is List<T>返回false(即无法将对象分配给List<T>变量)。


9
但是,这不会检测到子类型。看我的答案。接口的难度也更大:(
Jon Skeet

1
如果不是通用类型,则将引发对GetGenericTypeDefinition的调用。确保先检查一下。
Kilhoffer

85

我假设您不只是想知道类型是否为泛型,而是想知道一个对象是否为特定泛型类型的实例,而又不知道类型参数。

不幸的是,这并非十分简单。如果泛型类型是一个类(在这种情况下),这还不错,但是对于接口而言却很难。这是一个类的代码:

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>
    {
    }
}

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

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

我有一个偷偷摸摸的猜想,在这周围可能会有一些尴尬的情况,但是我现在找不到一个失败的案例。


2
刚刚发现了一个问题。它仅沿一行继承。如果在此过程中,如果您有一个同时具有基类所需接口的基类则这仅沿类路径进行。
Groxx 2011年

1
@Groxx:是的。我刚刚发现我在回答中确实提到了这一点:“如果泛型类型是一个类(在这种情况下就是这样),这还不错,但是对于接口而言却更加困难。这是一个类的代码”
乔恩Skeet

1
如果您没有办法认识<T>怎么办?例如,它可能是int或字符串,但是您不知道。这似乎会产生假阴性结果……因此您没有T可以使用,您只是在浏览某个对象的属性,而其中一个是列表。您怎么知道它是列表,所以您可以将其去皮?我的意思是,您在任何地方都没有T,也没有要使用的类型。您可以猜测每种类型(它是List <int>吗?还是List <string>?),但是您想知道的是此AA LIST吗?这个问题似乎很难回答。

@RiverC:是的,你说得对-这相当难回答,因为各种原因。如果您只是在谈论一个类,那还不错...您可以继续沿继承树走,看看您List<T>是以某种形式命中还是其他形式。如果包含接口,那确实很棘手。
乔恩·斯基特

3
您不能IsInstanceOfGenericTypeIsAssignableFrom而不是相等运算符(==)代替循环吗?
slawekwin

7

您可以使用动态算法使用较短的代码,这可能比纯反射要慢:

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

7

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

适用于:

  • 多个(通用)接口
  • 多个(通用)基类
  • 有一个过载,如果它返回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);


    }

0
return list.GetType().IsGenericType;

3
对另一个问题是正确的。对于这个问题,这是不正确的,因为它仅解决了(大大少于)一半的问题。
Groxx 2011年

1
Stan R的答案实际上确实回答了所提出的问题,但是OP真正的意思是“测试对象是否在C#中是特定的泛型类型”,对此答案确实是不完整的。
yoyo 2013年

人们不赞成我,因为我在“是”通用类型而不是“是”通用类型的背景下回答了这个问题。英语是我的第二种语言,这种语言上的细微差别经常让我感到困惑,为了辩护,OP并未明确要求针对特定类型进行测试,而标题中则要求“属于”通用类型...不确定为什么我应该为此投票赞成一个模棱两可的问题。
Stan R.

2
现在您知道了,您可以改善答案,使其更加具体和正确。
彼得·伊凡
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.