Check if a class is derived from a generic class

前端 未结 16 1362
太阳男子
太阳男子 2020-11-22 09:57

I have a generic class in my project with derived classes.

public class GenericClass : GenericInterface
{
}

public class Test : GenericCla         


        
相关标签:
16条回答
  • It seems to me that this implementation works in more cases (generic class and interface with or without initiated parameters, regardless of the number of child and parameters):

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

    Here are my 70 76 test cases:

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

    Classes and interfaces for testing :

    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 { }
    
    0 讨论(0)
  • 2020-11-22 10:04

    late to the game on this... i too have yet another permutation of JarodPar's answer.

    here's Type.IsSubClassOf(Type) courtesy of reflector:

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

    from that, we see that it's not doing anything too cray cray and is similar to JaredPar's iterative approach. so far, so good. here's my version (disclaimer: not thoroughly tested, so lemme know if you find issues)

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

    basically this is just an extension method to System.Type - i did this to intentionally limit the "thisType" Type to concrete Types, as my immediate usage is to LINQ query "where" predicates against Type objects. i'm sure all you smart folks out there could bang it down to an efficient, all-purpose static method if you need to :) the code does a few things the answer's code doesn't

    1. open's it up to to general "extension" - i'm considering inheritance (think classes) as well as implementation (interfaces); method and parameter names are changed to better reflect this
    2. input null-validation (meah)
    3. input of same type (a class cannot extend itself)
    4. short-circuit execution if Type in question is an interface; because GetInterfaces() returns all implemented interfaces (even ones implemented in super-classes), you can simply loop through that collection not having to climb the inheritance tree

    the rest is basically the same as JaredPar's code

    0 讨论(0)
  • 2020-11-22 10:06
    Type _type = myclass.GetType();
    PropertyInfo[] _propertyInfos = _type.GetProperties();
    Boolean _test = _propertyInfos[0].PropertyType.GetGenericTypeDefinition() 
    == typeof(List<>);
    
    0 讨论(0)
  • 2020-11-22 10:07

    Try this code

    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;
    }
    
    0 讨论(0)
  • 2020-11-22 10:08

    (Reposted due to a massive rewrite)

    JaredPar's code answer is fantastic, but I have a tip that would make it unnecessary if your generic types are not based on value type parameters. I was hung up on why the "is" operator would not work, so I have also documented the results of my experimentation for future reference. Please enhance this answer to further enhance its clarity.

    TIP:

    If you make certain that your GenericClass implementation inherits from an abstract non-generic base class such as GenericClassBase, you could ask the same question without any trouble at all like this:

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

    IsSubclassOf()

    My testing indicates that IsSubclassOf() does not work on parameterless generic types such as

    typeof(GenericClass<>)
    

    whereas it will work with

    typeof(GenericClass<SomeType>)
    

    Therefore the following code will work for any derivation of GenericClass<>, assuming you are willing to test based on SomeType:

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

    The only time I can imagine that you would want to test by GenericClass<> is in a plug-in framework scenario.


    Thoughts on the "is" operator

    At design-time C# does not allow the use of parameterless generics because they are essentially not a complete CLR type at that point. Therefore, you must declare generic variables with parameters, and that is why the "is" operator is so powerful for working with objects. Incidentally, the "is" operator also can not evaluate parameterless generic types.

    The "is" operator will test the entire inheritance chain, including interfaces.

    So, given an instance of any object, the following method will do the trick:

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

    This is sort of redundant, but I figured I would go ahead and visualize it for everybody.

    Given

    var t = new Test();
    

    The following lines of code would return true:

    bool test1 = IsTypeof<GenericInterface<SomeType>>(t);
    
    bool test2 = IsTypeof<GenericClass<SomeType>>(t);
    
    bool test3 = IsTypeof<Test>(t);
    

    On the other hand, if you want something specific to GenericClass, you could make it more specific, I suppose, like this:

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

    Then you would test like this:

    bool test1 = IsTypeofGenericClass<SomeType>(t);
    
    0 讨论(0)
  • 2020-11-22 10:15

    This can all be done easily with linq. This will find any types that are a subclass of generic base class GenericBaseType.

        IEnumerable<Type> allTypes = Assembly.GetExecutingAssembly().GetTypes();
    
        IEnumerable<Type> mySubclasses = allTypes.Where(t => t.BaseType != null 
                                                                && t.BaseType.IsGenericType
                                                                && t.BaseType.GetGenericTypeDefinition() == typeof(GenericBaseType<,>));
    
    0 讨论(0)
提交回复
热议问题