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.