How To Detect If Type is Another Generic Type
example:
public static void DoSomething<K,V>(IDictionary<K,V> items) {
items.Keys.Each(key => {
if (items[key] **is IEnumerable<?>**) { /* do something */ }
else { /* do something else */ }
}
Can this be done without using reflection? How do I say IEnumerable in C#? Should I just use IEnumerable since IEnumerable<> implements IEnumerable?
Thanks very much for this post. I wanted to provide a version of Konrad Rudolph's solution that has worked better for me. I had minor issues with that version, notably when testing if a Type is a nullable value 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);
}
The previously accepted answer is nice but it is wrong. Thankfully, the error is a small one. Checking for IEnumerable
is not enough if you really want to know about the generic version of the interface; there are a lot of classes that implement only the nongeneric interface. I'll give the answer in a minute. First, though, I'd like to point out that the accepted answer is overly complicated, since the following code would achieve the same under the given circumstances:
if (items[key] is IEnumerable)
This does even more because it works for each item separately (and not on their common subclass, V
).
Now, for the correct solution. This is a bit more complicated because we have to take the generic type IEnumerable`1
(that is, the type IEnumerable<>
with one type parameter) and 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 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 be overly concerned by the fact that this uses reflection. While it's true that this adds runtime overhead, so does the use of the is
operator.
Of course the above code is awfully constrained and could be expanded into a more generally applicable method, IsAssignableToGenericType
. The following implementation is slightly incorrect1 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 fails when the genericType
is the same as givenType
; for the same reason, it fails for nullable types, i.e.
IsAssignableToGenericType(typeof(List<int>), typeof(List<>)) == false
IsAssignableToGenericType(typeof(int?), typeof(Nullable<>)) == false
I’ve created a gist with a comprehensive suite of test cases.
A word of warning about generic types and using IsAssignableFrom()...
Say you have the following:
public class MyListBase<T> : IEnumerable<T> where T : ItemBase
{
}
public class MyItem : ItemBase
{
}
public class MyDerivedList : MyListBase<MyItem>
{
}
Calling IsAssignableFrom on the base list type or on the derived list type will return false, yet clearly MyDerivedList
inherits MyListBase<T>
. (A quick note for Jeff, generics absolutely must be wrapped in a code block or tildes to get the <T>
, otherwise it's omitted. Is this intended?) The problem stems from the fact that MyListBase<MyItem>
is treated as an entirely different type than MyListBase<T>
. The following article could explain this a little better. http://mikehadlow.blogspot.com/2006/08/reflecting-generics.html
Instead, try 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 new post which takes the generic recursion into account as well as interfaces is spot on. Very nice work. :)
/EDIT2: If a check is made on whether genericType is an interface, performance benefits could be realized. The check can be an if block around the current interface code, but if you're interested in using .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);
}
if (typeof(IEnumerable).IsAssignableFrom(typeof(V))) {