views:

48

answers:

1

Trying to find out if a provided Type is of a given generic type (with any generic types inside)

Let me Explain:

bool IsOfGenericType(Type baseType, Type sampleType)
{
    /// ...
}

Such that:

IsOfGenericType(typeof(Dictionary<,>), typeof(Dictionary<string, int>)); // True
IsOfGenericType(typeof(IDictionary<,>), typeof(Dictionary<string, int>)); // True
IsOfGenericType(typeof(IList<>), typeof(Dictionary<string,int>)); // False

However, I played with some reflection in the intermediate window, here were my results:

typeof(Dictionary<,>) is typeof(Dictionary<string,int>)
Type expected
typeof(Dictionary<string,int>) is typeof(Dictionary<string,int>)
Type expected
typeof(Dictionary<string,int>).IsAssignableFrom(typeof(Dictionary<,>))
false
typeof(Dictionary<string,int>).IsSubclassOf(typeof(Dictionary<,>))
false
typeof(Dictionary<string,int>).IsInstanceOfType(typeof(Dictionary<,>))
false
typeof(Dictionary<,>).IsInstanceOfType(typeof(Dictionary<string,int>))
false
typeof(Dictionary<,>).IsAssignableFrom(typeof(Dictionary<string,int>))
false
typeof(Dictionary<,>).IsSubclassOf(typeof(Dictionary<string,int>))
false
typeof(Dictionary<,>) is typeof(Dictionary<string,int>)
Type expected
typeof(Dictionary<string,int>) is typeof(Dictionary<string,int>)
Type expected
typeof(Dictionary<string,int>).IsAssignableFrom(typeof(Dictionary<,>))
false
typeof(Dictionary<string,int>).IsSubclassOf(typeof(Dictionary<,>))
false
typeof(Dictionary<string,int>).IsInstanceOfType(typeof(Dictionary<,>))
false
typeof(Dictionary<,>).IsInstanceOfType(typeof(Dictionary<string,int>))
false
typeof(Dictionary<,>).IsAssignableFrom(typeof(Dictionary<string,int>))
false
typeof(Dictionary<,>).IsSubclassOf(typeof(Dictionary<string,int>))
false

So now I'm at a loss because when you look at the base.Name on typeof(Dictionary) you get

Dictionary`2

Which is the same as typeof(Dictionary<,>).Name

+2  A: 

At first I thought that this should work:

bool IsOfGenericType(Type baseType, Type sampleType) { 
  return baseType.IsAssignableFrom(sampleType.GetGenericTypeDefinition());
} 

The GetGenericTypeDefinition method returns a version of the type without specified generic parameters. For example for type List<int> it will give you List<>.

But, unfortunately, this doesn't quite work, because the generic type definitions aren't assignable from each other (because they are not real types that you could use anywhere in your code). The IsAssignableFrom method works only for actual types. This means that we need to make additional step and specify some type parameters back to the generic type definitions and then we can check whether they are assignable.

You can fill the type parameters with System.Object and then compare them:

Type SpecifyObjectArgs(Type sample) {
  var types = new Type[sample.GetGenericArguments().Length];
  for(int i = 0; i < types.Length; i++) types[i] = typeof(System.Object);
  return sample.MakeGenericType(types);
}

var baseWithObjs = SpecifyObjectArgs(baseType);
var sampleWithObjs = SpecifyObjectArgs(sampleType.GetGenericTypeDefinition());
baseWithObjs.IsAssignableFrom(sampleWithObjs);

This turns the generic type definition back to a normal type, so you'll get for example IList<Object> and List<Object> which can be tested easily using IsAssignableFrom.

[There are still some limitations - see comments]

Tomas Petricek
\o/ Thankyou!!!
Aren
It may not be as simple as I thought :-) wait a second until I try that...
Tomas Petricek
Yeah, it was definitely more difficult than I thought. The current version should work fine though!
Tomas Petricek
This works fine if the actual type and test type are (virtually) identical, but what if it's not so clear-cut - i.e., what if you have a `MyClass<T, U>` implementing an `IEnumerable<U>`? Or even if you want to test a `Dictionary<TKey, TValue>` against an `IEnumerable<KeyValuePair<TKey, TValue>>`?
Aaronaught
@Aaronaught: Yeah, this is a limitation. I don't think there is a way to solve that without explicitly traversing the entire type hierarchy of the "sample" type (i.e. looking at all its base types and interfaces implemented by each base). My solution should at least work for all the samples above.
Tomas Petricek
It worked for my needs, thanks for the hard work! I will keep it in mind if i want to look at sub-types.
Aren