【问题标题】:Check if a class is derived from a generic class检查一个类是否派生自一个泛型类
【发布时间】:2009-01-19 14:10:18
【问题描述】:

我的项目中有一个带有派生类的泛型类。

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

public class Test : GenericClass<SomeType>
{
}

有什么方法可以查明Type 对象是否派生自GenericClass

t.IsSubclassOf(typeof(GenericClass<>))

不工作。

【问题讨论】:

    标签: c# generics reflection


    【解决方案1】:

    试试这个代码

    static bool IsSubclassOfRawGeneric(Type generic, Type toCheck) {
        while (toCheck != null && toCheck != typeof(object)) {
            var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck;
            if (generic == cur) {
                return true;
            }
            toCheck = toCheck.BaseType;
        }
        return false;
    }
    

    【讨论】:

    • 这是一段甜蜜的代码,我不得不说。 while 循环实现也避免了不必要的递归性能损失。这是对元通用问题的优雅而美丽的解决方案。
    • 如果 toCheck 类型不是类(即接口),while 循环不会中断。这将导致 NullReferenceException。
    • 如果 toCheck 是您要查找的泛型类型,这也将返回 true。
    • 这仅适用于具体类型继承...测试用例:bool expected = true; bool 实际 = Program.IsSubclassOfRawGeneric ( typeof ( IEnumerable ), typeof ( List ) ); Assert.AreEqual(预期,实际); //失败
    • 对于 netcore 使用 toCheck.GetTypeInfo().IsGenericTypetoCheck.GetTypeInfo().BaseType 而不是 toCheck.IsGenericTypetoCheck.BaseType
    【解决方案2】:

    (因大量改写而重发)

    JaredPar 的代码答案非常棒,但是如果您的泛型类型不是基于值类型参数,我有一个提示,那就是没有必要。我对为什么“is”运算符不起作用感到困惑,因此我还记录了我的实验结果以供将来参考。请增强此答案以进一步提高其清晰度。

    提示:

    如果您确定您的 GenericClass 实现继承自 GenericClassBase 等抽象非泛型基类,您可以毫无问题地问同样的问题:

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

    IsSubclassOf()

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

    typeof(GenericClass<>)
    

    虽然它可以与

    typeof(GenericClass<SomeType>)
    

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

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

    【讨论】:

    • +1 用于分析和测试。另外,您的回答对我来说非常有用。
    • 应该指出,编译器对 .IsSubclassOf(typeof(GenericClass)) 非常满意,但它并没有达到你的预期。
    • 但是如果 SomeType 在编译时未知怎么办?
    • 整个讨论的重点是当你只有一个Type 对象时。
    • @JonathanWood 这就是为什么我上面的答案是处理 typeof 运算符。根据文档:“typeof 运算符用于获取类型的 System.Type 对象。”
    【解决方案3】:

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

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

    【讨论】:

    • 我对 ResolveGenericTypeDefinition 方法感到困惑。 “shouldUseGenericType”变量实际上被赋值为:!parent.IsGenericType || parent.GetGenericTypeDefinition() == parent; 所以你用 if 语句的扩展替换该变量:if (parent.IsGenericType &amp;&amp; shouldUseGenericType),你得到if (parent.IsGenericType &amp;&amp; (!parent.IsGenericType || parent.GetGenericTypeDefinition() == parent)),然后减少到if (parent.IsGenericType &amp;&amp; parent.GetGenericTypeDefinition() == parent)) parent = parent.GetGenericTypeDefinition();
    • 这似乎什么也没做。如果这些是类似于int j = 0;if (j is an int &amp;&amp; j == 0){ j=0; } 的值类型,我的引用等于和值等于混淆了吗?我以为每个Type在内存中只有一个实例,所以指向同一个类型的两个变量实际上是指向内存中的同一个位置。
    • @MichaelBlackburn 点在 :) 我将其重构为:返回 parent.IsGenericType ? parent.GetGenericTypeDefinition() : 父级;
    • 如果已经和父级一样,直接返回!而且他调用 GetGenericTypeDef 的次数太多了。它只需要调用一次
    • 对不起,我会在下面添加一条新评论。
    【解决方案4】:

    在我看来,这种实现适用于更多情况(带有或不带有初始化参数的泛型类和接口,无论子级和参数的数量如何):

    public static class ReflexionExtension
    {
        public static bool IsSubClassOfGeneric(this Type child, Type parent)
        {
            if (child == parent)
                return false;
    
            if (child.IsSubclassOf(parent))
                return true;
    
            var parameters = parent.GetGenericArguments();
            var isParameterLessGeneric = !(parameters != null && parameters.Length > 0 &&
                ((parameters[0].Attributes & TypeAttributes.BeforeFieldInit) == TypeAttributes.BeforeFieldInit));
    
            while (child != null && child != typeof(object))
            {
                var cur = GetFullTypeDefinition(child);
                if (parent == cur || (isParameterLessGeneric && cur.GetInterfaces().Select(i => GetFullTypeDefinition(i)).Contains(GetFullTypeDefinition(parent))))
                    return true;
                else if (!isParameterLessGeneric)
                    if (GetFullTypeDefinition(parent) == cur && !cur.IsInterface)
                    {
                        if (VerifyGenericArguments(GetFullTypeDefinition(parent), cur))
                            if (VerifyGenericArguments(parent, child))
                                return true;
                    }
                    else
                        foreach (var item in child.GetInterfaces().Where(i => GetFullTypeDefinition(parent) == GetFullTypeDefinition(i)))
                            if (VerifyGenericArguments(parent, item))
                                return true;
    
                child = child.BaseType;
            }
    
            return false;
        }
    
        private static Type GetFullTypeDefinition(Type type)
        {
            return type.IsGenericType ? type.GetGenericTypeDefinition() : type;
        }
    
        private static bool VerifyGenericArguments(Type parent, Type child)
        {
            Type[] childArguments = child.GetGenericArguments();
            Type[] parentArguments = parent.GetGenericArguments();
            if (childArguments.Length == parentArguments.Length)
                for (int i = 0; i < childArguments.Length; i++)
                    if (childArguments[i].Assembly != parentArguments[i].Assembly || childArguments[i].Name != parentArguments[i].Name || childArguments[i].Namespace != parentArguments[i].Namespace)
                        if (!childArguments[i].IsSubclassOf(parentArguments[i]))
                            return false;
    
            return true;
        }
    }
    

    这是我的 70 76 个测试用例:

    [TestMethod]
    public void IsSubClassOfGenericTest()
    {
        Assert.IsTrue(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(BaseGeneric<>)), " 1");
        Assert.IsFalse(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(WrongBaseGeneric<>)), " 2");
        Assert.IsTrue(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), " 3");
        Assert.IsFalse(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(IWrongBaseGeneric<>)), " 4");
        Assert.IsTrue(typeof(IChildGeneric).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), " 5");
        Assert.IsFalse(typeof(IWrongBaseGeneric<>).IsSubClassOfGeneric(typeof(ChildGeneric2<>)), " 6");
        Assert.IsTrue(typeof(ChildGeneric2<>).IsSubClassOfGeneric(typeof(BaseGeneric<>)), " 7");
        Assert.IsTrue(typeof(ChildGeneric2<Class1>).IsSubClassOfGeneric(typeof(BaseGeneric<>)), " 8");
        Assert.IsTrue(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(BaseGeneric<Class1>)), " 9");
        Assert.IsFalse(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(WrongBaseGeneric<Class1>)), "10");
        Assert.IsTrue(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(IBaseGeneric<Class1>)), "11");
        Assert.IsFalse(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(IWrongBaseGeneric<Class1>)), "12");
        Assert.IsTrue(typeof(IChildGeneric).IsSubClassOfGeneric(typeof(IBaseGeneric<Class1>)), "13");
        Assert.IsFalse(typeof(BaseGeneric<Class1>).IsSubClassOfGeneric(typeof(ChildGeneric2<Class1>)), "14");
        Assert.IsTrue(typeof(ChildGeneric2<Class1>).IsSubClassOfGeneric(typeof(BaseGeneric<Class1>)), "15");
        Assert.IsFalse(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(ChildGeneric)), "16");
        Assert.IsFalse(typeof(IChildGeneric).IsSubClassOfGeneric(typeof(IChildGeneric)), "17");
        Assert.IsFalse(typeof(IBaseGeneric<>).IsSubClassOfGeneric(typeof(IChildGeneric2<>)), "18");
        Assert.IsTrue(typeof(IChildGeneric2<>).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), "19");
        Assert.IsTrue(typeof(IChildGeneric2<Class1>).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), "20");
        Assert.IsFalse(typeof(IBaseGeneric<Class1>).IsSubClassOfGeneric(typeof(IChildGeneric2<Class1>)), "21");
        Assert.IsTrue(typeof(IChildGeneric2<Class1>).IsSubClassOfGeneric(typeof(IBaseGeneric<Class1>)), "22");
        Assert.IsFalse(typeof(IBaseGeneric<Class1>).IsSubClassOfGeneric(typeof(BaseGeneric<Class1>)), "23");
        Assert.IsTrue(typeof(BaseGeneric<Class1>).IsSubClassOfGeneric(typeof(IBaseGeneric<Class1>)), "24");
        Assert.IsFalse(typeof(IBaseGeneric<>).IsSubClassOfGeneric(typeof(BaseGeneric<>)), "25");
        Assert.IsTrue(typeof(BaseGeneric<>).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), "26");
        Assert.IsTrue(typeof(BaseGeneric<Class1>).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), "27");
        Assert.IsFalse(typeof(IBaseGeneric<Class1>).IsSubClassOfGeneric(typeof(IBaseGeneric<Class1>)), "28");
        Assert.IsTrue(typeof(BaseGeneric2<Class1>).IsSubClassOfGeneric(typeof(IBaseGeneric<Class1>)), "29");
        Assert.IsFalse(typeof(IBaseGeneric<>).IsSubClassOfGeneric(typeof(BaseGeneric2<>)), "30");
        Assert.IsTrue(typeof(BaseGeneric2<>).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), "31");
        Assert.IsTrue(typeof(BaseGeneric2<Class1>).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), "32");
        Assert.IsTrue(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(BaseGenericA<,>)), "33");
        Assert.IsFalse(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(WrongBaseGenericA<,>)), "34");
        Assert.IsTrue(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "35");
        Assert.IsFalse(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(IWrongBaseGenericA<,>)), "36");
        Assert.IsTrue(typeof(IChildGenericA).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "37");
        Assert.IsFalse(typeof(IWrongBaseGenericA<,>).IsSubClassOfGeneric(typeof(ChildGenericA2<,>)), "38");
        Assert.IsTrue(typeof(ChildGenericA2<,>).IsSubClassOfGeneric(typeof(BaseGenericA<,>)), "39");
        Assert.IsTrue(typeof(ChildGenericA2<ClassA, ClassB>).IsSubClassOfGeneric(typeof(BaseGenericA<,>)), "40");
        Assert.IsTrue(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(BaseGenericA<ClassA, ClassB>)), "41");
        Assert.IsFalse(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(WrongBaseGenericA<ClassA, ClassB>)), "42");
        Assert.IsTrue(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(IBaseGenericA<ClassA, ClassB>)), "43");
        Assert.IsFalse(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(IWrongBaseGenericA<ClassA, ClassB>)), "44");
        Assert.IsTrue(typeof(IChildGenericA).IsSubClassOfGeneric(typeof(IBaseGenericA<ClassA, ClassB>)), "45");
        Assert.IsFalse(typeof(BaseGenericA<ClassA, ClassB>).IsSubClassOfGeneric(typeof(ChildGenericA2<ClassA, ClassB>)), "46");
        Assert.IsTrue(typeof(ChildGenericA2<ClassA, ClassB>).IsSubClassOfGeneric(typeof(BaseGenericA<ClassA, ClassB>)), "47");
        Assert.IsFalse(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(ChildGenericA)), "48");
        Assert.IsFalse(typeof(IChildGenericA).IsSubClassOfGeneric(typeof(IChildGenericA)), "49");
        Assert.IsFalse(typeof(IBaseGenericA<,>).IsSubClassOfGeneric(typeof(IChildGenericA2<,>)), "50");
        Assert.IsTrue(typeof(IChildGenericA2<,>).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "51");
        Assert.IsTrue(typeof(IChildGenericA2<ClassA, ClassB>).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "52");
        Assert.IsFalse(typeof(IBaseGenericA<ClassA, ClassB>).IsSubClassOfGeneric(typeof(IChildGenericA2<ClassA, ClassB>)), "53");
        Assert.IsTrue(typeof(IChildGenericA2<ClassA, ClassB>).IsSubClassOfGeneric(typeof(IBaseGenericA<ClassA, ClassB>)), "54");
        Assert.IsFalse(typeof(IBaseGenericA<ClassA, ClassB>).IsSubClassOfGeneric(typeof(BaseGenericA<ClassA, ClassB>)), "55");
        Assert.IsTrue(typeof(BaseGenericA<ClassA, ClassB>).IsSubClassOfGeneric(typeof(IBaseGenericA<ClassA, ClassB>)), "56");
        Assert.IsFalse(typeof(IBaseGenericA<,>).IsSubClassOfGeneric(typeof(BaseGenericA<,>)), "57");
        Assert.IsTrue(typeof(BaseGenericA<,>).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "58");
        Assert.IsTrue(typeof(BaseGenericA<ClassA, ClassB>).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "59");
        Assert.IsFalse(typeof(IBaseGenericA<ClassA, ClassB>).IsSubClassOfGeneric(typeof(IBaseGenericA<ClassA, ClassB>)), "60");
        Assert.IsTrue(typeof(BaseGenericA2<ClassA, ClassB>).IsSubClassOfGeneric(typeof(IBaseGenericA<ClassA, ClassB>)), "61");
        Assert.IsFalse(typeof(IBaseGenericA<,>).IsSubClassOfGeneric(typeof(BaseGenericA2<,>)), "62");
        Assert.IsTrue(typeof(BaseGenericA2<,>).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "63");
        Assert.IsTrue(typeof(BaseGenericA2<ClassA, ClassB>).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "64");
        Assert.IsFalse(typeof(BaseGenericA2<ClassB, ClassA>).IsSubClassOfGeneric(typeof(IBaseGenericA<ClassA, ClassB>)), "65");
        Assert.IsFalse(typeof(BaseGenericA<ClassB, ClassA>).IsSubClassOfGeneric(typeof(ChildGenericA2<ClassA, ClassB>)), "66");
        Assert.IsFalse(typeof(BaseGenericA2<ClassB, ClassA>).IsSubClassOfGeneric(typeof(BaseGenericA<ClassA, ClassB>)), "67");
        Assert.IsTrue(typeof(ChildGenericA3<ClassA, ClassB>).IsSubClassOfGeneric(typeof(BaseGenericB<ClassA, ClassB, ClassC>)), "68");
        Assert.IsTrue(typeof(ChildGenericA4<ClassA, ClassB>).IsSubClassOfGeneric(typeof(IBaseGenericB<ClassA, ClassB, ClassC>)), "69");
        Assert.IsFalse(typeof(ChildGenericA3<ClassB, ClassA>).IsSubClassOfGeneric(typeof(BaseGenericB<ClassA, ClassB, ClassC>)), "68-2");
        Assert.IsTrue(typeof(ChildGenericA3<ClassA, ClassB2>).IsSubClassOfGeneric(typeof(BaseGenericB<ClassA, ClassB, ClassC>)), "68-3");
        Assert.IsFalse(typeof(ChildGenericA3<ClassB2, ClassA>).IsSubClassOfGeneric(typeof(BaseGenericB<ClassA, ClassB, ClassC>)), "68-4");
        Assert.IsFalse(typeof(ChildGenericA4<ClassB, ClassA>).IsSubClassOfGeneric(typeof(IBaseGenericB<ClassA, ClassB, ClassC>)), "69-2");
        Assert.IsTrue(typeof(ChildGenericA4<ClassA, ClassB2>).IsSubClassOfGeneric(typeof(IBaseGenericB<ClassA, ClassB, ClassC>)), "69-3");
        Assert.IsFalse(typeof(ChildGenericA4<ClassB2, ClassA>).IsSubClassOfGeneric(typeof(IBaseGenericB<ClassA, ClassB, ClassC>)), "69-4");
        Assert.IsFalse(typeof(bool).IsSubClassOfGeneric(typeof(IBaseGenericB<ClassA, ClassB, ClassC>)), "70");
    }
    

    用于测试的类和接口:

    public class Class1 { }
    public class BaseGeneric<T> : IBaseGeneric<T> { }
    public class BaseGeneric2<T> : IBaseGeneric<T>, IInterfaceBidon { }
    public interface IBaseGeneric<T> { }
    public class ChildGeneric : BaseGeneric<Class1> { }
    public interface IChildGeneric : IBaseGeneric<Class1> { }
    public class ChildGeneric2<Class1> : BaseGeneric<Class1> { }
    public interface IChildGeneric2<Class1> : IBaseGeneric<Class1> { }
    
    public class WrongBaseGeneric<T> { }
    public interface IWrongBaseGeneric<T> { }
    
    public interface IInterfaceBidon { }
    
    public class ClassA { }
    public class ClassB { }
    public class ClassC { }
    public class ClassB2 : ClassB { }
    public class BaseGenericA<T, U> : IBaseGenericA<T, U> { }
    public class BaseGenericB<T, U, V> { }
    public interface IBaseGenericB<ClassA, ClassB, ClassC> { }
    public class BaseGenericA2<T, U> : IBaseGenericA<T, U>, IInterfaceBidonA { }
    public interface IBaseGenericA<T, U> { }
    public class ChildGenericA : BaseGenericA<ClassA, ClassB> { }
    public interface IChildGenericA : IBaseGenericA<ClassA, ClassB> { }
    public class ChildGenericA2<ClassA, ClassB> : BaseGenericA<ClassA, ClassB> { }
    public class ChildGenericA3<ClassA, ClassB> : BaseGenericB<ClassA, ClassB, ClassC> { }
    public class ChildGenericA4<ClassA, ClassB> : IBaseGenericB<ClassA, ClassB, ClassC> { }
    public interface IChildGenericA2<ClassA, ClassB> : IBaseGenericA<ClassA, ClassB> { }
    
    public class WrongBaseGenericA<T, U> { }
    public interface IWrongBaseGenericA<T, U> { }
    
    public interface IInterfaceBidonA { }
    

    【讨论】:

    • 这是唯一对我有用的解决方案。我找不到任何其他适用于具有多个类型参数的类的解决方案。谢谢。
    • 非常感谢您发布所有这些测试用例。我确实认为案例 68 和 69 应该是假而不是真,因为左边有 ClassB、ClassA,右边有 ClassA、ClassB。
    • 你是对的,@Grax。我现在没有时间进行更正,但我会在完成后立即更新我的帖子。我认为更正必须在“VerifyGenericArguments”方法中
    • @Grax :我找到了一些时间进行更正。我添加了 ClassB2 类,更改了 VerifyGenericArguments,并在调用 VerifyGenericArguments 时添加了一个控件。我还修改了案例 68 和 69,并添加了 68-2、68-3、68-4、69-2、69-3 和 69-4。
    • 谢谢先生。 +1 工作解决方案和大量测试用例(至少对我来说非常重要)。
    【解决方案5】:

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

    【讨论】:

    • JaredPar 代码中的while 涵盖了无限级别。
    • @jay ... 并避免递归。
    • @MarcL。这使用尾递归,因此编译器优化递归应该是微不足道的。
    【解决方案6】:

    这是我创建的一个小方法,用于检查对象是否派生自特定类型。非常适合我!

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

    【讨论】:

      【解决方案7】:

      这可能有点矫枉过正,但我​​使用如下扩展方法。他们检查接口和子类。它还可以返回具有指定泛型定义的类型。

      例如对于问题中的示例,它可以针对泛型接口和泛型类进行测试。返回的类型可以与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;
      }
      

      【讨论】:

      • 好帖子!很高兴用两种方法来解决问题。
      【解决方案8】:

      这一切都可以通过 linq 轻松完成。这将找到任何属于通用基类 GenericBaseType 子类的类型。

          IEnumerable<Type> allTypes = Assembly.GetExecutingAssembly().GetTypes();
      
          IEnumerable<Type> mySubclasses = allTypes.Where(t => t.BaseType != null 
                                                                  && t.BaseType.IsGenericType
                                                                  && t.BaseType.GetGenericTypeDefinition() == typeof(GenericBaseType<,>));
      

      【讨论】:

      • 这是唯一对我有用的解决方案。简单而优雅。谢谢。
      • 适用于。附带说明: == typeof(GenericBaseType ) 用于具有 2 种类型的泛型, == typeof(GenericBaseType ) 用于具有一种类型约束的泛型跨度>
      【解决方案9】:

      添加到@jaredpar 的答案中,这是我用来检查接口的内容:

      public static bool IsImplementerOfRawGeneric(this Type type, Type toCheck)
      {
          if (toCheck.GetTypeInfo().IsClass)
          {
              return false;
          }
      
          return type.GetInterfaces().Any(interfaceType =>
          {
              var current = interfaceType.GetTypeInfo().IsGenericType ?
                          interfaceType.GetGenericTypeDefinition() : interfaceType;
              return current == toCheck;
          });
      }
      
      public static bool IsSubTypeOfRawGeneric(this Type type, Type toCheck)
      {
          return type.IsInterface ?
                IsImplementerOfRawGeneric(type, toCheck)
              : IsSubclassOfRawGeneric(type, toCheck);
      }
      

      例如:

      Console.WriteLine(typeof(IList<>).IsSubTypeOfRawGeneric(typeof(IList<int>))); // true
      

      【讨论】:

      • 很好的补充。给你和 jaredpar 一个赞成票。我唯一的评论是,由于扩展方法,您颠倒了类型的位置(来自 jaredpar 的回答)。我把它作为一种扩展方法删除了,它让我有点失望。不是你的问题,而是我的问题。只是想让下一个人抬头。再次感谢。
      • @Tony,感谢您的提示,但下次更新答案。
      • @vexe,测试很重要,您的原始答案已被破坏,它仅适用于您在界面上进行了测试。其次,无论类型如何,我们都在浪费处理能力。
      【解决方案10】:

      简单的解决方案:只需创建第二个非泛型接口并将其添加到泛型类:

      public interface IGenericClass
      {
      }
      
      public class GenericClass<T> : GenericInterface<T>, IGenericClass
      {
      }
      

      然后,只需使用isasIsAssignableFrom 等以您喜欢的任何方式进行检查。

      if (thing is IGenericClass)
      {
          // Do work
      {
      

      显然只有当你有能力编辑泛型类(OP 似乎有)时才有可能,但它比使用神秘的扩展方法更优雅和可读。

      【讨论】:

      • 然而,仅仅检查某些东西是否属于IGenericClass 类型并不能保证GenericClassGenericInterface 确实被扩展或实现了。这意味着,您的编译器也不允许您访问泛型类的任何成员。
      • 我真的很惊讶这个解决方案是如此之低......我会做一些细微的改变 - 我会在内部制作界面。这种确保它是等价的。如果没有一些疯狂的代理,任何外部方都无法实现/扩展这个接口。而且这种搜索的可维护性是最重要的——上面列出了多少个解决方案?他们中有多少人没有这样的案例评论? ;)
      【解决方案11】:

      在 fir3rpho3nixx 和 David Schmitt 的上述出色答案的基础上,我修改了他们的代码并添加了 ShouldInheritOrImplementTypedGenericInterface 测试(最后一个)。

          /// <summary>
          /// Find out if a child type implements or inherits from the parent type.
          /// The parent type can be an interface or a concrete class, generic or non-generic.
          /// </summary>
          /// <param name="child"></param>
          /// <param name="parent"></param>
          /// <returns></returns>
          public static bool InheritsOrImplements(this Type child, Type parent)
          {
              var currentChild = parent.IsGenericTypeDefinition && child.IsGenericType ? child.GetGenericTypeDefinition() : child;
      
              while (currentChild != typeof(object))
              {
                  if (parent == currentChild || HasAnyInterfaces(parent, currentChild))
                      return true;
      
                  currentChild = currentChild.BaseType != null && parent.IsGenericTypeDefinition && 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 = parent.IsGenericTypeDefinition && childInterface.IsGenericType
                          ? childInterface.GetGenericTypeDefinition()
                          : childInterface;
      
                      return currentInterface == parent;
                  });
      
          }
      
          [Test]
          public void ShouldInheritOrImplementNonGenericInterface()
          {
              Assert.That(typeof(FooImplementor)
                  .InheritsOrImplements(typeof(IFooInterface)), Is.True);
          }
      
          [Test]
          public void ShouldInheritOrImplementGenericInterface()
          {
              Assert.That(typeof(GenericFooBase)
                  .InheritsOrImplements(typeof(IGenericFooInterface<>)), Is.True);
          }
      
          [Test]
          public void ShouldInheritOrImplementGenericInterfaceByGenericSubclass()
          {
              Assert.That(typeof(GenericFooImplementor<>)
                  .InheritsOrImplements(typeof(IGenericFooInterface<>)), Is.True);
          }
      
          [Test]
          public void ShouldInheritOrImplementGenericInterfaceByGenericSubclassNotCaringAboutGenericTypeParameter()
          {
              Assert.That(new GenericFooImplementor<string>().GetType()
                  .InheritsOrImplements(typeof(IGenericFooInterface<>)), Is.True);
          }
      
          [Test]
          public void ShouldNotInheritOrImplementGenericInterfaceByGenericSubclassNotCaringAboutGenericTypeParameter()
          {
              Assert.That(new GenericFooImplementor<string>().GetType()
                  .InheritsOrImplements(typeof(IGenericFooInterface<int>)), Is.False);
          }
      
          [Test]
          public void ShouldInheritOrImplementNonGenericClass()
          {
              Assert.That(typeof(FooImplementor)
                  .InheritsOrImplements(typeof(FooBase)), Is.True);
          }
      
          [Test]
          public void ShouldInheritOrImplementAnyBaseType()
          {
              Assert.That(typeof(GenericFooImplementor<>)
                  .InheritsOrImplements(typeof(FooBase)), Is.True);
          }
      
          [Test]
          public void ShouldInheritOrImplementTypedGenericInterface()
          {
              GenericFooImplementor<int> obj = new GenericFooImplementor<int>();
              Type t = obj.GetType();
      
              Assert.IsTrue(t.InheritsOrImplements(typeof(IGenericFooInterface<int>)));
              Assert.IsFalse(t.InheritsOrImplements(typeof(IGenericFooInterface<String>)));
          } 
      

      【讨论】:

        【解决方案12】:

        贾里德帕,

        如果我将 typeof(type) 作为 toCheck 传递,这对我不起作用。这是我所做的更改。

        static bool IsSubclassOfRawGeneric(Type generic, Type toCheck) {
            while (toCheck != typeof(object)) {
                var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck;
                  if (cur.IsGenericType && generic.GetGenericTypeDefinition() == cur.GetGenericTypeDefinition()) {
                    return true;
                }
                toCheck = toCheck.BaseType;
            }
            return false;
        }
        

        【讨论】:

        • JaredPar 的解决方案确实适用于 typeof(type&lt;&gt;)toCheck。此外,您确实需要 JaredPar 解决方案中的空值检查。此外,我不知道通过将他的解决方案中的cur == generic 替换为cur.IsGenericType &amp;&amp; generic.GetGenericTypeDefinition() == cur.GetGenericTypeDefinition(),除了将您的方法限制为仅适用于泛型类型之外,您还能实现什么。换句话说,像这样的任何事情都会失败,但有一个例外:IsSubclassOfRawGeneric(typeof(MyClass), typeof(MyClass&lt;&gt;))
        【解决方案13】:

        @EnocNRoll - Ananda Gopal 的回答很有趣,但如果实例没有事先实例化,或者您想检查泛型类型定义,我建议使用这种方法:

        public static bool TypeIs(this Type x, Type d) {
            if(null==d) {
                return false;
            }
        
            for(var c = x; null!=c; c=c.BaseType) {
                var a = c.GetInterfaces();
        
                for(var i = a.Length; i-->=0;) {
                    var t = i<0 ? c : a[i];
        
                    if(t==d||t.IsGenericType&&t.GetGenericTypeDefinition()==d) {
                        return true;
                    }
                }
            }
        
            return false;
        }
        

        并像这样使用它:

        var b = typeof(char[]).TypeIs(typeof(IList<>)); // true
        

        t(待测试)和d 都是泛型类型时有四种条件情况,t==d 涵盖了两种情况,它们是(1)既不是t 也不是d是泛型定义(2)它们都是泛型定义。其余情况是其中一个是通用定义,只有当d 已经是通用定义时,我们才有机会说td,反之则不行。

        它应该适用于您要测试的任意类或接口,并返回就像您使用 is 运算符测试该类型的实例一样。

        【讨论】:

          【解决方案14】:
          Type _type = myclass.GetType();
          PropertyInfo[] _propertyInfos = _type.GetProperties();
          Boolean _test = _propertyInfos[0].PropertyType.GetGenericTypeDefinition() 
          == typeof(List<>);
          

          【讨论】:

            【解决方案15】:

            你可以试试这个扩展

                public static bool IsSubClassOfGenericClass(this Type type, Type genericClass,Type t)
                {
                    return type.IsSubclassOf(genericClass.MakeGenericType(new[] { t }));
                }
            

            【讨论】:

              【解决方案16】:

              对此游戏迟到了......我也有另一个 JarodPar 答案的排列。

              这是由反射器提供的 Type.IsSubClassOf(Type):

                  public virtual bool IsSubclassOf(Type c)
                  {
                      Type baseType = this;
                      if (!(baseType == c))
                      {
                          while (baseType != null)
                          {
                              if (baseType == c)
                              {
                                  return true;
                              }
                              baseType = baseType.BaseType;
                          }
                          return false;
                      }
                      return false;
                  }
              

              从那里,我们看到它并没有做任何太笨拙的事情,并且类似于 JaredPar 的迭代方法。到目前为止,一切都很好。这是我的版本(免责声明:未经彻底测试,如果发现问题请告知)

                  public static bool IsExtension(this Type thisType, Type potentialSuperType)
                  {
                      //
                      // protect ya neck
                      //
                      if (thisType == null || potentialSuperType == null || thisType == potentialSuperType) return false;
              
                      //
                      // don't need to traverse inheritance for interface extension, so check/do these first
                      //
                      if (potentialSuperType.IsInterface)
                      {
                          foreach (var interfaceType in thisType.GetInterfaces())
                          {
                              var tempType = interfaceType.IsGenericType ? interfaceType.GetGenericTypeDefinition() : interfaceType;
              
                              if (tempType == potentialSuperType)
                              {
                                  return true;
                              }
                          }
                      }
              
                      //
                      // do the concrete type checks, iterating up the inheritance chain, as in orignal
                      //
                      while (thisType != null && thisType != typeof(object))
                      {
                          var cur = thisType.IsGenericType ? thisType.GetGenericTypeDefinition() : thisType;
              
                          if (potentialSuperType == cur)
                          {
                              return true;
                          }
              
                          thisType = thisType.BaseType;
                      }
                      return false;
                  }
              

              基本上,这只是 System.Type 的扩展方法——我这样做是为了故意将“thisType”类型限制为具体类型,因为我的直接用途是针对 Type 对象查询“where”谓词。我敢肯定,如果您需要的话,所有聪明的人都可以将其归结为一种高效的通用静态方法:)代码做了一些答案的代码没有做的事情

              1. 打开它到一般的“扩展”——我正在考虑继承(想想类) 以及实现(接口);方法和参数名称 更改以更好地反映这一点
              2. 输入空值验证(嗯)
              3. 相同类型的输入(类不能扩展自身)
              4. 如果所讨论的类型是接口,则执行短路;因为 GetInterfaces() 返回所有实现的接口(甚至是在超类中实现的接口),所以您可以简单地循环遍历该集合,而不必爬上继承树

              其余的和JaredPar的代码基本一样

              【讨论】:

                猜你喜欢
                • 2016-07-22
                • 1970-01-01
                • 2010-12-04
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2020-06-07
                相关资源
                最近更新 更多