Check if a class is derived from a generic class

Viewed 178266

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.

16 Answers

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

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

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

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

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

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

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;
}
Type _type = myclass.GetType();
PropertyInfo[] _propertyInfos = _type.GetProperties();
Boolean _test = _propertyInfos[0].PropertyType.GetGenericTypeDefinition() 
== typeof(List<>);
Related