在 C # 中测试 object 是否是泛型类型

我想执行一个测试,如果一个对象是泛型类型。我尝试过以下方法,但没有成功:

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

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

90640 次浏览

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

return list.GetType().IsGenericType;

如果你想检查它是否是一个通用的 List<T>:

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

正如 Jon 指出的,这将检查确切的类型等价性。返回 false并不一定意味着 list is List<T>返回 false(即对象不能被赋值给 List<T>变量)。

return list.GetType().IsGenericType;

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

不幸的是,事情并不是那么简单。如果泛型类型是一个类(在本例中就是这样) ,那么还不算太糟糕,但是对于接口来说就比较困难了。下面是一个类的代码:

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

我怀疑这里面可能有一些棘手的边缘案例,但是我现在找不到一个失败的案例。

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

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

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

合作伙伴:

  • 多个(泛型)接口
  • 多个(泛型)基类
  • 有一个重载,如果返回 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;
    }
    }
    

Here's a test to demonstrate the (basic) functionality:

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




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