How to determine if a type is another typical 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 to say IEnumerable in C #? Should I just use IEnumerable since IEnumerable <> implements IEnumerable?

+22
c #
Sep 16 '08 at 17:07
source share
9 answers

The previously accepted answer is good, but it is wrong. Fortunately, the error is small. Checking for IEnumerable not enough if you really want to know about the general version of the interface; There are many classes that implement only the neon interface. I will give an answer in a minute. Firstly, I would like to note that the accepted answer is too complicated, as the following code will achieve the same in the given circumstances:

 if (items[key] is IEnumerable) 

This does even more, because it works for each element separately (and not for the general subclass of V ).

Now, for the right decision. This is a bit more complicated because we need to take the generic type IEnumerable`1 (i.e. type IEnumerable<> with one type parameter) and enter the correct 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 easily verify the correctness of this code:

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

gives:

 True False 

Do not worry too much about using reflection. Although it is true that this adds overhead at runtime, the is operator is also used.

Of course, the above code is terribly limited and can be extended to the more public IsAssignableToGenericType method. The following implementation is somewhat incorrect 1 and I left it here for historical purposes only. Do not use it . Instead, James 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 genericType matches givenType ; for the same reason, it fails for types with a null value, i.e.

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

Ive created a gist with a complete set of test cases .

+34
Sep 16 '08 at 18:33
source share

Thanks so much for this post. I wanted to provide a version of Konrad Rudolph's solution that worked better for me. I had minor issues with this version, especially when testing if type is a value type with a null value:

 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); } 
+82
Jul 02 '09 at 15:25
source share

A word of warning about common types and the use of 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 from the base type of the list or from the derived type of the list returns false, but explicitly MyDerivedList inherits MyListBase<T> . (A quick note for Jeff, generics must absolutely be wrapped in a code block or tildes in order to get <T> , otherwise it will drop. Is this intended?) The problem is that MyListBase<MyItem> considered as a completely different type of MyListBase<T> . The following article may 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 that takes into account general recursion as well as interfaces. Very nice work. :)

/ EDIT2: If it is checked whether genericType is an interface, performance benefits can be realized. The check may be an if block around the current interface code, but if you are interested in using .NET 3.5, my friend suggests 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); } 
+5
Sep 16 '08 at 18:43
source share
 if (typeof(IEnumerable).IsAssignableFrom(typeof(V))) { 
+4
Sep 16 '08 at 17:11
source share

Thanks for the great info. To be sure, I reworked this into an extension method and reduced it to one 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 it can be easily called with

sometype.IsAssignableToGenericType (TypeOf (MyGenericType <>))

+4
Dec 30 '11 at 20:56
source share

I would use overload:

 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 */ }); } 
+1
Sep 16 '08 at 18:18
source share

I'm not sure I understand what you mean here. Do you want to know if an object has any common type or do you want to check if it is a specific generic type ? Or do you just want to know if it is enumerable?

I do not think the first is possible. The second is certainly possible, just treat it like any other type. For the third, just check it against IEnumerable, as you suggested.

In addition, you cannot use the 'is' operator for types.

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

See this type question for more details. Perhaps this will help you.

0
Sep 16 '08 at 17:23
source share

Do you want to check the Type.IsInstanceOfType method

0
Sep 16 '08 at 18:10
source share

I used to think that such a situation could be resolved similarly to @Thomas Danecker, but it adds 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 now I noticed that this does not work unless I specify the template arguments of the first method explicitly. This is clearly not configured for each element in the dictionary, but it may be a kind of solution for a weak person.

I would be very grateful if someone could point out something wrong that I could do here.

0
Dec 24 '08 at 7:44
source share



All Articles