我有一个泛型类在我的项目与派生类。

public class GenericClass<T> : GenericInterface<T>
{
}

public class Test : GenericClass<SomeType>
{
}

是否有任何方法可以查明Type对象是否派生自GenericClass?

t.IsSubclassOf(typeof(GenericClass<>))

不管用。


当前回答

Type _type = myclass.GetType();
PropertyInfo[] _propertyInfos = _type.GetProperties();
Boolean _test = _propertyInfos[0].PropertyType.GetGenericTypeDefinition() 
== typeof(List<>);

其他回答

更新后的答案

这个方法检查typeA是否等于、继承(class: class)、实现(class: interface)或扩展(interface: interface) typeB。它接受泛型和非泛型接口和类。

public static bool Satisfies(Type typeA, Type typeB)
{
    var types = new List<Type>(typeA.GetInterfaces());
    for (var t = typeA; t != null; t = t.BaseType)
    {
        types.Add(t);
    }
    return types.Any(t =>
        t == typeB ||
            t.IsGenericType && (t.GetGenericTypeDefinition() == typeB));
}

在下面使用,它通过了76个测试中的74个@Xav987的答案(它没有通过测试'68-3'和'69-3',但我认为这些测试暗示List<长颈鹿>是List<动物>的子类,我不认为它是。例如,List<长颈鹿>不能施放到List<动物>,参见https://stackoverflow.com/a/9891849/53252。)

public static bool IsSubClassOfGeneric(this Type typeA, Type typeB)
{
    if (typeA == typeB)
    {
        return false;
    }
    return Satisfies(typeA, typeB);
}

例子:

using System.Collections;
using System.Numerics;

void ShowSatisfaction(Type typeA, Type typeB)
{
    var satisfied = Satisfies(typeA, typeB);
    Console.WriteLine($"{satisfied}: [{typeA}]  satisfies  [{typeB}]");
}

ShowSatisfaction(typeof(object), typeof(string));
ShowSatisfaction(typeof(string), typeof(object));
ShowSatisfaction(typeof(string), typeof(IEnumerable));
ShowSatisfaction(typeof(string), typeof(IEnumerable<>));
ShowSatisfaction(typeof(string), typeof(IEnumerable<char>));
ShowSatisfaction(typeof(string), typeof(IEnumerable<int>));
ShowSatisfaction(typeof(int), typeof(object));
ShowSatisfaction(typeof(int), typeof(IComparable));
ShowSatisfaction(typeof(IReadOnlyDictionary<,>), typeof(IReadOnlyCollection<>));
ShowSatisfaction(typeof(bool), typeof(INumber<>));
ShowSatisfaction(typeof(int), typeof(INumber<>));
ShowSatisfaction(typeof(IBinaryInteger<>), typeof(IShiftOperators<,>));
ShowSatisfaction(typeof(IBinaryInteger<int>), typeof(IShiftOperators<,>));
ShowSatisfaction(typeof(IBinaryInteger<int>), typeof(IShiftOperators<int, int>));

输出:

False: [System.Object]  satisfies  [System.String]
True: [System.String]  satisfies  [System.Object]
True: [System.String]  satisfies  [System.Collections.IEnumerable]
True: [System.String]  satisfies  [System.Collections.Generic.IEnumerable`1[T]]
True: [System.String]  satisfies  [System.Collections.Generic.IEnumerable`1[System.Char]]
False: [System.String]  satisfies  [System.Collections.Generic.IEnumerable`1[System.Int32]]
True: [System.Int32]  satisfies  [System.Object]
True: [System.Int32]  satisfies  [System.IComparable]
True: [System.Collections.Generic.IReadOnlyDictionary`2[TKey,TValue]]  satisfies  [System.Collections.Generic.IReadOnlyCollection`1[T]]
False: [System.Boolean]  satisfies  [System.Numerics.INumber`1[TSelf]]
True: [System.Int32]  satisfies  [System.Numerics.INumber`1[TSelf]]
True: [System.Numerics.IBinaryInteger`1[TSelf]]  satisfies  [System.Numerics.IShiftOperators`2[TSelf,TResult]]
True: [System.Numerics.IBinaryInteger`1[System.Int32]]  satisfies  [System.Numerics.IShiftOperators`2[TSelf,TResult]]
True: [System.Numerics.IBinaryInteger`1[System.Int32]]  satisfies  [System.Numerics.IShiftOperators`2[System.Int32,System.Int32]]

INumber<>的例子来自。net 7预览5。

以前的回答

它可能是多余的,但我使用如下扩展方法。它们检查接口和子类。它还可以返回具有指定泛型定义的类型。

例如,对于问题中的例子,它可以测试泛型接口以及泛型类。返回的类型可以与GetGenericArguments一起使用,以确定泛型参数类型是“SomeType”。

/// <summary>
/// Checks whether this type has the specified definition in its ancestry.
/// </summary>   
public static bool HasGenericDefinition(this Type type, Type definition)
{
    return GetTypeWithGenericDefinition(type, definition) != null;
}

/// <summary>
/// Returns the actual type implementing the specified definition from the
/// ancestry of the type, if available. Else, null.
/// </summary>
public static Type GetTypeWithGenericDefinition(this Type type, Type definition)
{
    if (type == null)
        throw new ArgumentNullException("type");
    if (definition == null)
        throw new ArgumentNullException("definition");
    if (!definition.IsGenericTypeDefinition)
        throw new ArgumentException(
            "The definition needs to be a GenericTypeDefinition", "definition");

    if (definition.IsInterface)
        foreach (var interfaceType in type.GetInterfaces())
            if (interfaceType.IsGenericType
                && interfaceType.GetGenericTypeDefinition() == definition)
                return interfaceType;

    for (Type t = type; t != null; t = t.BaseType)
        if (t.IsGenericType && t.GetGenericTypeDefinition() == definition)
            return t;

    return null;
}

(由于大量重写而重新发布)

JaredPar的代码回答非常棒,但是我有一个技巧,如果您的泛型类型不是基于值类型参数,那么就没有必要这样做。我一直纠结于为什么“is”操作符不能工作,所以我还记录了我的实验结果,以供将来参考。请加强这个答案,以进一步提高其清晰度。

TIP:

如果你确定你的GenericClass实现继承自一个抽象的非泛型基类,比如GenericClassBase,你可以毫不费力地问同样的问题,就像这样:

typeof(Test).IsSubclassOf(typeof(GenericClassBase))

IsSubclassOf()

我的测试表明IsSubclassOf()不适用于无参数的泛型类型,例如

typeof(GenericClass<>)

而它将与

typeof(GenericClass<SomeType>)

因此,以下代码将适用于GenericClass<>的任何派生,假设您愿意基于SomeType进行测试:

typeof(Test).IsSubclassOf(typeof(GenericClass<SomeType>))

我能想象到您希望通过GenericClass<>进行测试的唯一情况是在插件框架场景中。


对“is”操作符的思考

在设计时,c#不允许使用无参数泛型,因为那时它们本质上还不是一个完整的CLR类型。因此,必须声明带参数的泛型变量,这就是“is”操作符在处理对象时如此强大的原因。顺便说一句,“is”操作符也不能计算无参数的泛型类型。

“is”操作符将测试整个继承链,包括接口。

因此,给定任何对象的实例,下面的方法就可以做到:

bool IsTypeof<T>(object t)
{
    return (t is T);
}

这有点多余,但我想我可以把它可视化给大家。

鉴于

var t = new Test();

下面几行代码将返回true:

bool test1 = IsTypeof<GenericInterface<SomeType>>(t);

bool test2 = IsTypeof<GenericClass<SomeType>>(t);

bool test3 = IsTypeof<Test>(t);

另一方面,如果你想要一些特定于GenericClass的东西,你可以让它更具体,我想,像这样:

bool IsTypeofGenericClass<SomeType>(object t)
{
    return (t is GenericClass<SomeType>);
}

然后你可以这样测试:

bool test1 = IsTypeofGenericClass<SomeType>(t);

我研究了其中一些样本,发现它们在某些情况下是缺乏的。这个版本适用于所有类型的泛型:类型、接口及其类型定义。

public static bool InheritsOrImplements(this Type child, Type parent)
{
    parent = ResolveGenericTypeDefinition(parent);

    var currentChild = child.IsGenericType
                           ? child.GetGenericTypeDefinition()
                           : child;

    while (currentChild != typeof (object))
    {
        if (parent == currentChild || HasAnyInterfaces(parent, currentChild))
            return true;

        currentChild = currentChild.BaseType != null
                       && currentChild.BaseType.IsGenericType
                           ? currentChild.BaseType.GetGenericTypeDefinition()
                           : currentChild.BaseType;

        if (currentChild == null)
            return false;
    }
    return false;
}

private static bool HasAnyInterfaces(Type parent, Type child)
{
    return child.GetInterfaces()
        .Any(childInterface =>
        {
            var currentInterface = childInterface.IsGenericType
                ? childInterface.GetGenericTypeDefinition()
                : childInterface;

            return currentInterface == parent;
        });
}

private static Type ResolveGenericTypeDefinition(Type parent)
{
    var shouldUseGenericType = true;
    if (parent.IsGenericType && parent.GetGenericTypeDefinition() != parent)
        shouldUseGenericType = false;

    if (parent.IsGenericType && shouldUseGenericType)
        parent = parent.GetGenericTypeDefinition();
    return parent;
}

下面是单元测试:

protected interface IFooInterface
{
}

protected interface IGenericFooInterface<T>
{
}

protected class FooBase
{
}

protected class FooImplementor
    : FooBase, IFooInterface
{
}

protected class GenericFooBase
    : FooImplementor, IGenericFooInterface<object>
{

}

protected class GenericFooImplementor<T>
    : FooImplementor, IGenericFooInterface<T>
{
}


[Test]
public void Should_inherit_or_implement_non_generic_interface()
{
    Assert.That(typeof(FooImplementor)
        .InheritsOrImplements(typeof(IFooInterface)), Is.True);
}

[Test]
public void Should_inherit_or_implement_generic_interface()
{
    Assert.That(typeof(GenericFooBase)
        .InheritsOrImplements(typeof(IGenericFooInterface<>)), Is.True);
}

[Test]
public void Should_inherit_or_implement_generic_interface_by_generic_subclass()
{
    Assert.That(typeof(GenericFooImplementor<>)
        .InheritsOrImplements(typeof(IGenericFooInterface<>)), Is.True);
}

[Test]
public void Should_inherit_or_implement_generic_interface_by_generic_subclass_not_caring_about_generic_type_parameter()
{
    Assert.That(new GenericFooImplementor<string>().GetType()
        .InheritsOrImplements(typeof(IGenericFooInterface<>)), Is.True);
}

[Test]
public void Should_not_inherit_or_implement_generic_interface_by_generic_subclass_not_caring_about_generic_type_parameter()
{
    Assert.That(new GenericFooImplementor<string>().GetType()
        .InheritsOrImplements(typeof(IGenericFooInterface<int>)), Is.False);
}

[Test]
public void Should_inherit_or_implement_non_generic_class()
{
    Assert.That(typeof(FooImplementor)
        .InheritsOrImplements(typeof(FooBase)), Is.True);
}

[Test]
public void Should_inherit_or_implement_any_base_type()
{
    Assert.That(typeof(GenericFooImplementor<>)
        .InheritsOrImplements(typeof(FooBase)), Is.True);
}

下面是我创建的一个小方法,用于检查对象是否派生于特定类型。对我来说太棒了!

internal static bool IsDerivativeOf(this Type t, Type typeToCompare)
{
    if (t == null) throw new NullReferenceException();
    if (t.BaseType == null) return false;

    if (t.BaseType == typeToCompare) return true;
    else return t.BaseType.IsDerivativeOf(typeToCompare);
}

JaredPar的代码可以工作,但只适用于一个继承级别。要获得无限级别的继承,请使用以下代码

public bool IsTypeDerivedFromGenericType(Type typeToCheck, Type genericType)
{
    if (typeToCheck == typeof(object))
    {
        return false;
    }
    else if (typeToCheck == null)
    {
        return false;
    }
    else if (typeToCheck.IsGenericType && typeToCheck.GetGenericTypeDefinition() == genericType)
    {
        return true;
    }
    else
    {
        return IsTypeDerivedFromGenericType(typeToCheck.BaseType, genericType);
    }
}