Friday, August 18, 2006

Reflecting Generics

I had an interesting problem today, how do you find out if an object is a subclass of a generic type? If you've simply got an instance of a generic type...
List<int> myobject = new List<int>();
You can find that myobject is a List<> by using the method GetGenericTypeDefinition():
if(myobject.GetType().GetGenericTypeDefinition() == typeof(List<>)) { ... }
But say you've got a type that inherits from a generic type:
public class A : List<int> { }
Now if you've got an instance of A:
A myA = new A();
You can't call GetGenericTypeDefinition(), it complains that myA.GetType() is in an incorrect state. Similarly, asking this:
myA.GetType().IsSubclassOf(typeof(List<>))
returns false because myA is a subclass of List<int>, but not List<>. I couldn't find any way of doing this without manually walking up the class hierarchy. In the end I gave in and wrote a this little recursive function:
public 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);
}
I guess where I was going wrong was thinking that List<int> 'inherits' from List<>, but of course it doesn't. It's an entirely unrelated hierarchy. Generics are not inheritance. I spent some time digging through the newsgroups looking for an answer to this and there's quite a lot of confusion around this issue. A really common misconception is to think that, for example, List<int> inherits from List<object> and that you can downcast like this..
List<int> myIntList = new List<int>();
List<object> myObjectList = (List<object>)myIntList;
It doesn't work.

2 comments:

secretGeek said...

i was reading a juval lowy article called 'best practices for generics' or soemthing like that this morning and he described when, how, why and why not to do this sort of thing. i've forgotten it all since, unfortunately.

Niko Will said...

very nice article, exactly what i am searching for. Thanks.