Ian Horwill commented in my previous blog:
“Hi Venkat, enjoyed the show. Was intrigued by your examination of type info for generic types and I
think I found a bug - see my blog post for today. Also I am not sure I understand what you mean
towards the end when you talk about constraining a generic type to a class or its superclass.
How would this be useful? You would not be able to use any subclass-specific behaviour without resorting
to type checking (similar to covariance not allowed for parameters of overriding methods).”
The little quirk that he stepped on is very interesting and is a bit deeper, actually. He has a class
Derived<T> which inherits from Base<T>. He then has code to display details of these classes.
What you would expect from this example is the following relationship:

However, what it actually is the following:

The base of Derived<T> (which is a generic type definition is not a generic type definition!
Here is a code that examines this:
namespace TestIt2
{
public class Base<T> { }
public class Derived<T> : Base<T> { }
public class Program
{
public static void Main()
{
Type theType = typeof(Derived<int>);
Info(theType);
Type baseType = theType.BaseType;
Info(baseType);
Type genericTypeDef = theType.GetGenericTypeDefinition();
Info(genericTypeDef);
Type baseOfGenericTypeDef = genericTypeDef.BaseType;
Info(baseOfGenericTypeDef);
Type genericTypeDefOfBaseOfGenericTypeDef =
baseOfGenericTypeDef.GetGenericTypeDefinition();
Info(genericTypeDefOfBaseOfGenericTypeDef);
Type baseOfGenericTypeDefOfBaseOfGenericTypeDef =
genericTypeDefOfBaseOfGenericTypeDef.BaseType;
Info(baseOfGenericTypeDefOfBaseOfGenericTypeDef);
}
private static void Info(Type type)
{
Console.WriteLine("---------------");
Console.WriteLine("Name={0}", type.Name);
Console.WriteLine("Full Name={0}", type.FullName);
Console.WriteLine("HashCode={0}", type.GetHashCode());
Console.WriteLine("IsGenericType={0}", type.IsGenericType);
Console.WriteLine("IsGenericTypeDefinition={0}",
type.IsGenericTypeDefinition);
Console.WriteLine("Generic Parameters:");
foreach (Type arg in type.GetGenericArguments())
{
Console.WriteLine("\t" + arg);
}
}
}
}
The output from above code is:
---------------
Name=Derived`1
Full Name=TestIt2.Derived`1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=ne
utral, PublicKeyToken=b77a5c561934e089]]
HashCode=10564272
IsGenericType=True
IsGenericTypeDefinition=False
Generic Parameters:
System.Int32
---------------
Name=Base`1
Full Name=TestIt2.Base`1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutr
al, PublicKeyToken=b77a5c561934e089]]
HashCode=10564152
IsGenericType=True
IsGenericTypeDefinition=False
Generic Parameters:
System.Int32
---------------
Name=Derived`1
Full Name=TestIt2.Derived`1
HashCode=10564024
IsGenericType=True
IsGenericTypeDefinition=True
Generic Parameters:
T
---------------
Name=Base`1
Full Name=
HashCode=10563904
IsGenericType=True
IsGenericTypeDefinition=False
Generic Parameters:
T
---------------
Name=Base`1
Full Name=TestIt2.Base`1
HashCode=10563784
IsGenericType=True
IsGenericTypeDefinition=True
Generic Parameters:
T
---------------
Name=Object
Full Name=System.Object
HashCode=2031066136
IsGenericType=False
IsGenericTypeDefinition=False
Generic Parameters:
As you can see the hash code for Base'1 in the section where "Full Name = " is blank is not the same as
the hash code in the following section. Also notice that the IsGenericTypeDefinition is false in the section
of interest.
I hope someone from Microsoft can shed some light on this.
I will answer his second part in the next blog.