[ACCEPTED]-How To Detect If Type is Another Generic Type-c#

Accepted answer
Score: 144

Thanks very much for this post. I wanted 5 to provide a version of Konrad Rudolph's 4 solution that has worked better for me. I 3 had minor issues with that version, notably 2 when testing if a Type is a nullable value 1 type:

public static bool IsAssignableToGenericType(Type givenType, Type genericType)
{
    var interfaceTypes = givenType.GetInterfaces();

    foreach (var it in interfaceTypes)
    {
        if (it.IsGenericType && it.GetGenericTypeDefinition() == genericType)
            return true;
    }

    if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)
        return true;

    Type baseType = givenType.BaseType;
    if (baseType == null) return false;

    return IsAssignableToGenericType(baseType, genericType);
}
Score: 42

The previously accepted answer is nice but it is wrong. Thankfully, the 28 error is a small one. Checking for IEnumerable is not 27 enough if you really want to know about 26 the generic version of the interface; there 25 are a lot of classes that implement only 24 the nongeneric interface. I'll give the 23 answer in a minute. First, though, I'd like 22 to point out that the accepted answer is 21 overly complicated, since the following 20 code would achieve the same under the given 19 circumstances:

if (items[key] is IEnumerable)

This does even more because 18 it works for each item separately (and not 17 on their common subclass, V).

Now, for the 16 correct solution. This is a bit more complicated 15 because we have to take the generic type 14 IEnumerable`1 (that is, the type IEnumerable<> with one type parameter) and 13 inject the right generic argument:

static bool IsGenericEnumerable(Type t) {
    var genArgs = t.GetGenericArguments();
    if (genArgs.Length == 1 &&
            typeof(IEnumerable<>).MakeGenericType(genArgs).IsAssignableFrom(t))
        return true;
    else
        return t.BaseType != null && IsGenericEnumerable(t.BaseType);
}

You can 12 test the correctness of this code easily:

var xs = new List<string>();
var ys = new System.Collections.ArrayList();
Console.WriteLine(IsGenericEnumerable(xs.GetType()));
Console.WriteLine(IsGenericEnumerable(ys.GetType()));

yields:

True
False

Don't 11 be overly concerned by the fact that this 10 uses reflection. While it's true that this 9 adds runtime overhead, so does the use of 8 the is operator.

Of course the above code is 7 awfully constrained and could be expanded 6 into a more generally applicable method, IsAssignableToGenericType. The 5 following implementation is slightly incorrect1 4 and I’ll leave it here for historic purposes only. Do not use it. Instead, James has provided an excellent, correct implementation in his answer.

public static bool IsAssignableToGenericType(Type givenType, Type genericType) {
    var interfaceTypes = givenType.GetInterfaces();

    foreach (var it in interfaceTypes)
        if (it.IsGenericType)
            if (it.GetGenericTypeDefinition() == genericType) return true;

    Type baseType = givenType.BaseType;
    if (baseType == null) return false;

    return baseType.IsGenericType &&
        baseType.GetGenericTypeDefinition() == genericType ||
        IsAssignableToGenericType(baseType, genericType);
}

1 It 3 fails when the genericType is the same as givenType; for the 2 same reason, it fails for nullable types, i.e.

IsAssignableToGenericType(typeof(List<int>), typeof(List<>)) == false
IsAssignableToGenericType(typeof(int?), typeof(Nullable<>)) == false

I’ve 1 created a gist with a comprehensive suite of test cases.

Score: 5

A word of warning about generic types and 20 using IsAssignableFrom()...

Say you have 19 the following:

public class MyListBase<T> : IEnumerable<T> where T : ItemBase
{
}

public class MyItem : ItemBase
{
}

public class MyDerivedList : MyListBase<MyItem>
{
}

Calling IsAssignableFrom on 18 the base list type or on the derived list 17 type will return false, yet clearly MyDerivedList inherits 16 MyListBase<T>. (A quick note for Jeff, generics absolutely 15 must be wrapped in a code block or tildes to 14 get the <T>, otherwise it's omitted. Is this 13 intended?) The problem stems from the fact 12 that MyListBase<MyItem> is treated as an entirely different 11 type than MyListBase<T>. The following article could 10 explain this a little better. http://mikehadlow.blogspot.com/2006/08/reflecting-generics.html

Instead, try 9 the following recursive function:

    public static bool IsDerivedFromGenericType(Type givenType, Type genericType)
    {
        Type baseType = givenType.BaseType;
        if (baseType == null) return false;
        if (baseType.IsGenericType)
        {
            if (baseType.GetGenericTypeDefinition() == genericType) return true;
        }
        return IsDerivedFromGenericType(baseType, genericType);
    }

/EDIT: Konrad's 8 new post which takes the generic recursion 7 into account as well as interfaces is spot 6 on. Very nice work. :)

/EDIT2: If a check 5 is made on whether genericType is an interface, performance 4 benefits could be realized. The check can 3 be an if block around the current interface 2 code, but if you're interested in using 1 .NET 3.5, a friend of mine offers the following:

    public static bool IsAssignableToGenericType(Type givenType, Type genericType)
    {
        var interfaces = givenType.GetInterfaces().Where(it => it.IsGenericType).Select(it => it.GetGenericTypeDefinition());
        var foundInterface = interfaces.FirstOrDefault(it => it == genericType);
        if (foundInterface != null) return true;

        Type baseType = givenType.BaseType;
        if (baseType == null) return false;

        return baseType.IsGenericType ?
            baseType.GetGenericTypeDefinition() == genericType :
            IsAssignableToGenericType(baseType, genericType);
    }
Score: 4
if (typeof(IEnumerable).IsAssignableFrom(typeof(V))) {

0

Score: 4

Thanks for the great info. For convienience, I've 3 refactored this into an extension method 2 and reduced it to a single statement.

public static bool IsAssignableToGenericType(this Type givenType, Type genericType)
{
  return givenType.GetInterfaces().Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == genericType) ||
         givenType.BaseType != null && (givenType.BaseType.IsGenericType && givenType.BaseType.GetGenericTypeDefinition() == genericType ||
                                        givenType.BaseType.IsAssignableToGenericType(genericType));
}

Now 1 it can be easily called with:

sometype.IsAssignableToGenericType(typeof(MyGenericType<>))

Score: 1

I'd use overloading:

public static void DoSomething<K,V>(IDictionary<K,V> items)
  where V : IEnumerable
{
   items.Keys.Each(key => { /* do something */ });
}

public static void DoSomething<K,V>(IDictionary<K,V> items)
{
   items.Keys.Each(key => { /* do something else */ });
}

0

Score: 0

I'm not sure I understand what you mean 9 here. Do you want to know if the object 8 is of any generic type or do you want to test if it is a 7 specific generic type? Or do you just want to know if is enumerable?

I 6 don't think the first is possible. The second 5 is definitely possible, just treat it as 4 any other type. For the third, just test 3 it against IEnumerable as you suggested.

Also, you 2 cannot use the 'is' operator on types.

// Not allowed
if (string is Object)
  Foo();
// You have to use 
if (typeof(object).IsAssignableFrom(typeof(string))
  Foo();

See 1 this question about types for more details. Maybe it'll help you.

Score: 0

I used to think such a situation may be 10 solvable in a way similar to @Thomas Danecker's 9 solution, but adding another template argument:

public static void DoSomething<K, V, U>(IDictionary<K,V> items)
    where V : IEnumerable<U> { /* do something */ }
public static void DoSomething<K, V>(IDictionary<K,V> items)
                             { /* do something else */ }

But 8 I noticed now that it does't work unless 7 I specify the template arguments of the 6 first method explicitly. This is clearly 5 not customized per each item in the dictionary, but 4 it may be a kind of poor-man's solution.

I 3 would be very thankful if someone could 2 point out anything incorrect I might have 1 done here.

More Related questions