Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determine if object is an instance of a generic base class, any generic type

Tags:

c#

reflection

I need to test if a value is an instance of a generic base class, without knowing the generic type parameter. Using the MSDN example as the base of my example, this is what I'd like to accomplish:

using System;

public class Class1<T> { }
public class DerivedC1 : Class1<int> { }

class IsSubclassTest
{
   public static void Main()
   {
      Console.WriteLine(
          "DerivedC1 subclass of Class1: {0}",
          typeof(DerivedC1).IsSubclassOf(typeof(Class1<>)) // <- Here.
      );
   }
}

While this is syntactically correct, it always yields false. If I remove the generic type parameter, it works as expected (returns true).

How can I test if a class type is a subclass of a generic base class, without knowing its generic type parameter as such?

like image 635
John Weisz Avatar asked May 12 '16 10:05

John Weisz


2 Answers

The problem is that DrevidedC1 is not a sublcass of Class1<T>, it's a subclass of Class1<int>. Make sure you understand this subtle diference; Class1<T> is a open type (T can be anything, it hasn't been set) while DerivedC1 extends a closed type Class1<int> (it's not open in T anymore, T is set to int and only int). So when you do the following:

 typeof(DerivedC1).IsSubclassOf(typeof(Class1<>))

The answer is evidently false.

What you need to do is check if the generic type definition of DerivedC1's base type (think of it as the corresponding open generic type of Class1<int>) equals Class1<T> which it clearly does.

The correct code is therefore:

typeof(DerivedC1).BaseType.GetGenericTypeDefinition() == typeof(Class1<>));

Or better yet, as Matías Fidemraizer states in his answer:

typeof(DerivedC1).BaseType.GetGenericTypeDefinition().IsAssignableFrom(typeof(Class1<>)));
like image 121
InBetween Avatar answered Oct 06 '22 00:10

InBetween


There's special methods on Type for this sort of thing. As far as I can see, you'll need to walk up your base-types and check each in turn until you either (a) hit a match or (b) get to the top of the inheritance hierarchy (i.e. System.Object).

As such, the following (recursive) extension method:

public static class TypeExtensions
{
    public static bool IsDerivedFromGenericParent(this Type type, Type parentType)
    {
        if(!parentType.IsGenericType)
        {
            throw new ArgumentException("type must be generic", "parentType");
        }
        if(type == null || type == typeof(object))
        {
            return false;
        }
        if(type.IsGenericType && type.GetGenericTypeDefinition() == parentType)
        {
            return true;
        }
        return type.BaseType.IsDerivedFromGenericParent(parentType)
            || type.GetInterfaces().Any(t=>t.IsDerivedFromGenericParent(parentType));
    }
}

will allow you to do the following

typeof(DerivedC1).IsDerivedFromGenericParent(typeof(Class1<>))

...and will also work if you test something derived from DerivedC1.

like image 33
spender Avatar answered Oct 06 '22 00:10

spender