351

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

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

public class Test : GenericClass<SomeType>
{
}

Is there any way to find out if a Type object is derived from GenericClass?

t.IsSubclassOf(typeof(GenericClass<>))

does not work.

Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
bernhardrusch
  • 11,670
  • 12
  • 48
  • 59

16 Answers16

501

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;
}
xanadont
  • 7,493
  • 6
  • 36
  • 49
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • 4
    This is a sweet piece of code, I have to say. The while loop implementation avoids the unnecessary recursion performance hit also. It's an elegant and beautiful solution to a meta-generic question. – EnocNRoll - AnandaGopal Pardue Jan 19 '09 at 18:26
  • 2
    I have added this method to my ReflectionUtils static class in my framework, and I have also adapted it as an extension method for object by defining toCheck within the method as Type toCheck = obj.GetType(); given "this object obj" is the first parameter. – EnocNRoll - AnandaGopal Pardue Jan 23 '09 at 22:22
  • 13
    The while loop will not break if the toCheck type is not a class (ie, interface). This will cause a NullReferenceException. – JD Courtoy Dec 23 '09 at 21:54
  • could you have a look over this: http://stackoverflow.com/questions/2477577/how-can-i-recognize-a-generic-class for VB.NET 2 is there something similar? – serhio Mar 19 '10 at 15:42
  • 3
    This will also return true if toCheck is the generic type you are looking for. – oillio May 05 '10 at 04:04
  • 16
    This only works for concrete type inheritance... Test case: bool expected = true; bool actual = Program.IsSubclassOfRawGeneric ( typeof ( IEnumerable<> ), typeof ( List ) ); Assert.AreEqual ( expected, actual ); // fails – Bobby Jul 13 '10 at 18:17
  • It would probably be beneficial to cache the typeof(object) call somewhere so it's not repeatedly being called in the while loop. – rossisdead Nov 11 '11 at 15:27
  • 2
    As @oillio stated, this does not behave the same as IsSubclass, is should include: if (genericType == toCheck) { return false} on the first line – Robert Sirre Feb 27 '12 at 16:45
  • 2
    I added Type[] interfaces = cur.GetInterfaces(); and checked these as well for my situation, if that helps anyone – Jon Sep 14 '12 at 10:06
  • @JaredPar Nice solution. Depending on the case, only the `IsConstructedGenericType` may be available and the code would need to be adapted. – Lorenz Lo Sauer Jul 25 '16 at 10:51
  • 5
    For **netcore** use `toCheck.GetTypeInfo().IsGenericType` and `toCheck.GetTypeInfo().BaseType` instead of `toCheck.IsGenericType` and `toCheck.BaseType` – amd May 06 '17 at 20:04
104

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

I worked through some of these samples and found they were lacking in some cases. This version works with all kinds of generics: types, interfaces and type definitions thereof.

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

Here are the unit tests also:

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);
}
David Schmitt
  • 58,259
  • 26
  • 121
  • 165
fir3rpho3nixx
  • 391
  • 3
  • 2
  • This was the answer I was looking for! Works for concrete types AND for interfaces, thanks :-) – Menno Deij - van Rijswijk Sep 03 '12 at 11:21
  • 2
    I'm a puzzled about the ResolveGenericTypeDefinition method. The "shouldUseGenericType" variable is really assigned the value: `!parent.IsGenericType || parent.GetGenericTypeDefinition() == parent;` So you replace that variable with the expansion of the if statement: `if (parent.IsGenericType && shouldUseGenericType)` and you get `if (parent.IsGenericType && (!parent.IsGenericType || parent.GetGenericTypeDefinition() == parent))` which then reduces to `if (parent.IsGenericType && parent.GetGenericTypeDefinition() == parent)) parent = parent.GetGenericTypeDefinition();` – Michael Blackburn Dec 04 '12 at 23:17
  • 2
    Which would seem to do nothing. If these were value types that would be akin to `int j = 0;` `if (j is an int && j == 0)` `{ j=0; }` Am I getting my reference equals and value equals mixed up? I thought there was only one instance of each Type in memory, so two variables that point to the same type are in fact pointing to the same location in memory. – Michael Blackburn Dec 04 '12 at 23:20
  • 1
    @MichaelBlackburn spot on :) i refactored this to just be: return parent.IsGenericType ? parent.GetGenericTypeDefinition() : parent; – AaronHS Feb 19 '13 at 09:15
  • 3
    if its already the same as the parent, just return it! and he's calling GetGenericTypeDef too many times. It just needs to be called once – AaronHS Feb 19 '13 at 09:16
  • 1
    Sorry, I will add a new comment below. – Menno Deij - van Rijswijk Mar 12 '13 at 11:48
36

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 { }
Xav987
  • 1,182
  • 1
  • 15
  • 17
  • 5
    This is the only solution that worked for me. I couldn't find any other solutions that worked with classes that have multiple type parameters. Thank you. – Connor Clark Jan 03 '15 at 04:24
  • 1
    I really appreciated your posting all of these test cases. I do think that cases 68 and 69 should be false instead of true, as you have ClassB, ClassA on the left and ClassA, ClassB on the right. – Grax32 Jan 07 '15 at 22:58
  • You're right, @Grax. I do not have time to make the correction now, but I will update my post as soon as it's done. I think the correction must be in "VerifyGenericArguments" method – Xav987 Feb 03 '15 at 16:37
  • 1
    @Grax : I found some time to make the correction. I added the ClassB2 class, I changed VerifyGenericArguments, and I added a control at the call of VerifyGenericArguments. I also modified the cases 68 and 69, and added 68-2, 68-3, 68-4, 69-2, 69-3 and 69-4. – Xav987 Mar 16 '15 at 16:24
  • 1
    Thank you sir. +1 for working solution AND the tremendous amount of test cases (tremendous to me, at least). – Eldoïr Apr 24 '18 at 10:45
  • What an excellent answer. I only wish I could upvote it further. – Ian Kemp Jun 28 '19 at 06:18
  • Just adding my thanks for providing the only solution that worked for me, and my appreciation for the test cases. Well done. – JamesQMurphy Feb 25 '21 at 01:35
  • Thank you for the comprehensive tests! I think tests '68-3' and '69-3' should expect `False`. I think `True` implies List is a subclass of List but I don't think it is. E.g., List cannot be cast to a List, see https://stackoverflow.com/a/9891849/53252. Although people may want or expect the current behaviour. – codybartfast Jun 25 '22 at 06:46
  • @codybartfast : I disagree, `ChildGenericA3` is a subclass of `BaseGenericB` and `ChildGenericA4` is a subclass of `IBaseGenericB`. Or maybe I misunderstood your comment? – Xav987 Jul 04 '22 at 13:42
  • @Xav987 Yes, that's where we disagree :-) I don't think B2 being a subclass of B implies `Gen` is a subclass of `Gen` because 1) `typeof(Gen).IsSubclassOf(typeof(Gen))` is `False`. 2) `Gen` cannot be assigned to `Gen`, so `Gen` cannot be used where `Gen` is required. 3) Given `interface Inf {}`, `class B2 : B, Inf {}` and `class Gen where T : Inf {}` then `Gen` is not a subclass of `Gen` because `Gen` cannot exist in this case. (But I'm no expert on this stuff) – codybartfast Jul 07 '22 at 05:29
  • I agree, `Gen` is not necessarily a subclass of `Gen`, even if `B2` is a subclass of `B`. But in our case, `ChildGenericA3` is a subclass of `BaseGenericB`. You can see `public class ChildGenericA3 : BaseGenericB { }` in the class declarations. The same for 69-3. – Xav987 Jul 07 '22 at 11:55
11

JaredPar's code works but only for one level of inheritance. For unlimited levels of inheritance, use the following code

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);
    }
}
user53564
  • 297
  • 2
  • 7
10

Here's a little method I created for checking that a object is derived from a specific type. Works great for me!

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

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<,>));
DVK
  • 2,726
  • 1
  • 17
  • 20
  • This was the only solution that worked for me. Simple and elegant. Thank you. – silkfire Apr 19 '17 at 23:09
  • 1
    Works for. Just a side note: == typeof(GenericBaseType **<,>** ) for generics with 2 types, == typeof(GenericBaseType **<>** ) for generics with one type contraint – dba Jul 19 '21 at 14:19
8

Simple solution: just create and add a second, non-generic interface to the generic class:

public interface IGenericClass
{
}

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

Then just check for that in any way you like using is, as, IsAssignableFrom, etc.

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

Obviously only possible if you have the ability to edit the generic class (which the OP seems to have), but it's a bit more elegant and readable than using a cryptic extension method.

kad81
  • 10,712
  • 3
  • 38
  • 44
  • 1
    however, just checking whether something is of type `IGenericClass` will not guarantee you that `GenericClass` or `GenericInterface` are actually extended or implemented. This means, your compiler also won't allow you to access any members of the generic class. – Felix K. Jun 24 '18 at 20:03
  • I'm actually surprised this solution is so low... I'd make a slight change - I'd make the interface internal. This kind of ensures it's an equivalent. Without some crazy proxying no external party would be able to implement/extend this interface. And the maintainability of such search is over the top - how many solutions are listed above? And how many of them doesn't have a it-doesn't-work-for-such-case comment? ;) – marchewek Sep 11 '20 at 12:25
7

Added to @jaredpar's answer, here's what I use to check for interfaces:

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

Ex:

Console.WriteLine(typeof(IList<>).IsSubTypeOfRawGeneric(typeof(IList<int>))); // true
johnny 5
  • 19,893
  • 50
  • 121
  • 195
vexe
  • 5,433
  • 12
  • 52
  • 81
  • Good addition. Gave both you and jaredpar an upvote. My only comment is you reversed the position of the types(from jaredpar's answer) because of the extension method. I removed it as an extension method and it threw me off for a bit. Not your problem but mine. Just wanted to give the next person heads up. Thanks again. – Tony Jun 18 '15 at 17:02
  • @Tony, thanks for the tip but next time Update the answer. – johnny 5 Sep 20 '17 at 01:13
  • @vexe, testing is important your original answer is broken, it only works because you tested it on an interface. Secondly, you we're wasting processing power by running this function regardless of the type. – johnny 5 Sep 20 '17 at 01:15
7

Updated Answer

This method checks if typeA equals, inherts (class : class), implements (class : interface) or extends (interface : interface) typeB. It accepts generic and non-generic interfaces and classes.

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

Used in the following it passes 74 of the 76 of the tests @Xav987's answer (It doesn't pass tests '68-3' and '69-3' but I think these tests imply List<Giraffe> is a subclass of List<Animal> and I don't think it is. E.g., List<Giraffe> cannot be cast to a List<Animal>, see https://stackoverflow.com/a/9891849/53252.)

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

Examples:

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

Output:

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<> examples are from .NET 7 preview 5.

Previous Answer

It might be overkill but I use extension methods like the following. They check interfaces as well as subclasses. It can also return the type that has the specified generic definition.

E.g. for the example in the question it can test against generic interface as well as generic class. The returned type can be used with GetGenericArguments to determine that the generic argument type is "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;
}
codybartfast
  • 7,323
  • 3
  • 21
  • 25
6

Building on the excellent answer above by fir3rpho3nixx and David Schmitt, I have modified their code and added the ShouldInheritOrImplementTypedGenericInterface test (last one).

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

JaredPar,

This did not work for me if I pass typeof(type<>) as toCheck. Here's what I changed.

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;
}
Cirem
  • 840
  • 1
  • 11
  • 15
  • JaredPar's solution indeed works for `typeof(type<>)` as `toCheck`. Also you really need the null check as in JaredPar's solution. Furthermore, I do not know what else you're achieving by replacing `cur == generic` in his solution by `cur.IsGenericType && generic.GetGenericTypeDefinition() == cur.GetGenericTypeDefinition()` other than restricting your method to work only for generic types. In other words, anything like this fails with an exception: `IsSubclassOfRawGeneric(typeof(MyClass), typeof(MyClass<>))` – nawfal May 09 '13 at 17:22
2

@EnocNRoll - Ananda Gopal 's answer is interesting, but in case an instance is not instantiated beforehand or you want to check with a generic type definition, I'd suggest this method:

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

and use it like:

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

There are four conditional cases when both t(to be tested) and d are generic types and two cases are covered by t==d which are (1)neither t nor d is a generic definition or (2) both of them are generic definitions. The rest cases are one of them is a generic definition, only when d is already a generic definition we have the chance to say a t is a d but not vice versa.

It should work with arbitrary classes or interfaces you want to test, and returns what as if you test an instance of that type with the is operator.

Ken Kin
  • 4,503
  • 3
  • 38
  • 76
0

You can try this extension

    public static bool IsSubClassOfGenericClass(this Type type, Type genericClass,Type t)
    {
        return type.IsSubclassOf(genericClass.MakeGenericType(new[] { t }));
    }
Yaseen
  • 598
  • 6
  • 7
0

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

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