Today was I was on holiday, doing a « faire le ponte » between the weekend and the public holiday toussaint (all saints day). I had chance to get lots of little jobs out of the way, such as tidying the garden and making the Christmas puddings. In between that I installed Visual Studio 2005 RTM on my laptop as apposed to a virtual machine.<?xml:namespace prefix = o ns = “urn:schemas-microsoft-com:office:office” />/o:p

 /o:p

So I thought I’d also take some time to write up some notes on things that I have noticed reflecting over generic types in framework version 2.0. In, what I shall call for briefness, .net20 the instance property FullName from the Type class is sometimes null. At first I thought this was a bug, but there are a specific set of circumstances when it is null and a fairly logical reason for it being null. This is noted in this bug report; however I don’t think the bug report makes the reason very clear, so I’ll write my own explication./o:p

 /o:p

This change is important as it is a breaking change for any application that is designed to reflect over other applications and makes use of the Type class’ FullName property. NDoc is one of these applications; in fact it has 88 references to the FullName property, which will probably slow down its .net20 release./o:p

 /o:p

There are several ways to obtain a Type object, if you are reflecting over another application or assembly the most likely way you’ll do this is to use the Assembly type’s GetTypes method. In this case the FullName property is present and correct whether the type is generic or not. However if this type is generic and one of its members uses another generic type and if type object is queried to get a type object for the second generic type then its FullName property will be null./o:p

 /o:p

Than explain has a lot ifs in it, so make its time to look at an example to make things clearer. First we get a list of types from, just to make life easier. Then we find the Predicate type and write out its details. After this we get the predicate type again but this time from the Find method on the list object:/o:p

 /o:p

Assembly mscorlib = Assembly.Load(“mscorlib”);/o:p

Type[] mscorlibTypes = mscorlib.GetTypes();/o:p

List<Type> mscorlibTypesList = new List<Type>(mscorlibTypes);/o:p

 /o:p

Type predicateType1 = mscorlibTypesList.Find(delegate(Type t) { return t.Name == “Predicate1"</SPAN><FONT color=#000000>; });<o:p></o:p></FONT></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; mso-layout-grid-align: none"><SPAN lang=EN-GB style="FONT-SIZE: 8pt; mso-ansi-language: EN-GB; mso-no-proof: yes"><FONT color=#000000>WriteTypeDetails(</FONT><SPAN style="COLOR: maroon">"Predicate - method 1"</SPAN><FONT color=#000000>, predicateType1);<o:p></o:p></FONT></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; mso-layout-grid-align: none"><SPAN lang=EN-GB style="FONT-SIZE: 8pt; mso-ansi-language: EN-GB; mso-no-proof: yes"><o:p><FONT color=#000000>&nbsp;</FONT></o:p></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; mso-layout-grid-align: none"><SPAN lang=EN-GB style="FONT-SIZE: 8pt; COLOR: teal; mso-ansi-language: EN-GB; mso-no-proof: yes">Type</SPAN><SPAN lang=EN-GB style="FONT-SIZE: 8pt; mso-ansi-language: EN-GB; mso-no-proof: yes"><FONT color=#000000> listType = mscorlibTypesList.Find(</FONT><SPAN style="COLOR: blue">delegate</SPAN><FONT color=#000000>(</FONT><SPAN style="COLOR: teal">Type</SPAN><FONT color=#000000> t) { </FONT><SPAN style="COLOR: blue">return</SPAN><FONT color=#000000> t.Name == </FONT><SPAN style="COLOR: maroon">"List1”; });/o:p

Type predicateType2 = GetFindMethodsParameterType(listType);/o:p

WriteTypeDetails(“Predicate - method 2”, predicateType2);/o:p

 /o:p

The WriteTypeDetails produces the following output:/o:p

=====================/o:p

Predicate - method 1/o:p

=====================/o:p

AssemblyQualifiedName: “System.Predicate1, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"<o:p></o:p></FONT></FONT></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-GB style="mso-ansi-language: EN-GB"><FONT size=2><FONT face=Verdana>Namespace: "System"<o:p></o:p></FONT></FONT></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-GB style="mso-ansi-language: EN-GB"><FONT size=2><FONT face=Verdana>Name: "Predicate1”/o:p

FullName: “System.Predicate1"<o:p></o:p></FONT></FONT></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-GB style="mso-ansi-language: EN-GB"><FONT size=2><FONT face=Verdana>TypeHandle.Value: "2031516484"<o:p></o:p></FONT></FONT></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-GB style="mso-ansi-language: EN-GB"><FONT size=2><FONT face=Verdana>GUID: "538820bd-5276-3f6e-ac28-ce2ab6ff9756"<o:p></o:p></FONT></FONT></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-GB style="mso-ansi-language: EN-GB"><o:p><FONT face=Verdana size=2>&nbsp;</FONT></o:p></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-GB style="mso-ansi-language: EN-GB"><FONT size=2><FONT face=Verdana>=====================<o:p></o:p></FONT></FONT></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-GB style="mso-ansi-language: EN-GB"><FONT size=2><FONT face=Verdana>Predicate - method 2<o:p></o:p></FONT></FONT></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-GB style="mso-ansi-language: EN-GB"><FONT size=2><FONT face=Verdana>=====================<o:p></o:p></FONT></FONT></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-GB style="mso-ansi-language: EN-GB"><FONT size=2><FONT face=Verdana>AssemblyQualifiedName: ""<o:p></o:p></FONT></FONT></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-GB style="mso-ansi-language: EN-GB"><FONT size=2><FONT face=Verdana>Namespace: "System"<o:p></o:p></FONT></FONT></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-GB style="mso-ansi-language: EN-GB"><FONT size=2><FONT face=Verdana>Name: "Predicate1”/o:p

FullName: “”/o:p

TypeHandle.Value: “2031797524”/o:p

GUID: “538820bd-5276-3f6e-ac28-ce2ab6ff9756”/o:p

 /o:p

As you can see in the first method of obtain the Predicate type both the FullName and the AssemblyQualifiedName properties are as expect but in the second case they are both null. Also you can see from the TypeHandle values that the type objects are both different. /o:p

 /o:p

So why is this? This is because the type objects differ in a subtle but important way, the first instance of the object the Type object is fully generic, it represents the generic Predicate type that has not been constrained in anyway. In the second instance the type object is constrained to be the same type as the type that created, but we do not know what type this is yet. It is this constrained yet unknown status that leads the FullName and the AssemblyQualifiedName properties to be null. I believe this is important during serialisation where it is important to be able to uniquely identity a type, in the second instance is impossible to uniquely identity the unknown type, so the FullName and AssemblyQualifiedName properties are set to null to represent this./o:p

 /o:p

Perhaps this will become clear still if I show that if the generic type parameter is specified, then both methods of obtaining the type parameter yield the same result. /o:p

 /o:p

Type predicateType3 = typeof(Predicate<int>);/o:p

WriteTypeDetails(“Predicate - method 3”, predicateType3);/o:p

 /o:p

Type intListType = typeof(List<int>);/o:p

Type predicateType4 = GetFindMethodsParameterType(intListType);/o:p

WriteTypeDetails(“Predicate - method 4”, predicateType4);/o:p

 /o:p

This code will output the following:/o:p

 /o:p

=====================/o:p

Predicate - method 3/o:p

=====================/o:p

AssemblyQualifiedName: “System.Predicate1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"<o:p></o:p></FONT></FONT></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-GB style="mso-ansi-language: EN-GB"><FONT size=2><FONT face=Verdana>Namespace: "System"<o:p></o:p></FONT></FONT></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-GB style="mso-ansi-language: EN-GB"><FONT size=2><FONT face=Verdana>Name: "Predicate1”/o:p

FullName: “System.Predicate1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, Public<o:p></o:p></FONT></FONT></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-GB style="mso-ansi-language: EN-GB"><FONT size=2><FONT face=Verdana>KeyToken=b77a5c561934e089]]"<o:p></o:p></FONT></FONT></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-GB style="mso-ansi-language: EN-GB"><FONT size=2><FONT face=Verdana>TypeHandle.Value: "2031770252"<o:p></o:p></FONT></FONT></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-GB style="mso-ansi-language: EN-GB"><FONT size=2><FONT face=Verdana>GUID: "538820bd-5276-3f6e-ac28-ce2ab6ff9756"<o:p></o:p></FONT></FONT></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-GB style="mso-ansi-language: EN-GB"><o:p><FONT face=Verdana size=2>&nbsp;</FONT></o:p></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-GB style="mso-ansi-language: EN-GB"><FONT size=2><FONT face=Verdana>=====================<o:p></o:p></FONT></FONT></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-GB style="mso-ansi-language: EN-GB"><FONT size=2><FONT face=Verdana>Predicate - method 4<o:p></o:p></FONT></FONT></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-GB style="mso-ansi-language: EN-GB"><FONT size=2><FONT face=Verdana>=====================<o:p></o:p></FONT></FONT></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-GB style="mso-ansi-language: EN-GB"><FONT size=2><FONT face=Verdana>AssemblyQualifiedName: "System.Predicate1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089”/o:p

Namespace: “System”/o:p

Name: “Predicate1"<o:p></o:p></FONT></FONT></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-GB style="mso-ansi-language: EN-GB"><FONT size=2><FONT face=Verdana>FullName: "System.Predicate1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]”/o:p

TypeHandle.Value: “2031770252”/o:p

GUID: “538820bd-5276-3f6e-ac28-ce2ab6ff9756”/o:p

 /o:p

Here we can see that the both type objects have exactly the same output, and from the TypeHandle we can see they are the same object./o:p

 /o:p

My suggested further reading for this topic is to look at the MSDN documentation for the IsGenericType property which defines all the invariants for generic types./o:p

 /o:p

The sharp eyed amongst you will have noticed that are a couple of methods that I have not defined, they are listed below and a full download with all the code is available here./o:p

 /o:p

    static Type GetFindMethodsParameterType(Type listType)/o:p

    {/o:p

        MethodInfo findMethod = listType.GetMethod(“Find”);/o:p

        ParameterInfo[] findParameters = findMethod.GetParameters();/o:p

        Type predicateType2 = findParameters[0].ParameterType;/o:p

        return predicateType2;/o:p

    }/o:p

 /o:p

    static void WriteTypeDetails(string title, Type type)/o:p

    {/o:p

        Console.WriteLine(”=====================”);/o:p

        Console.WriteLine(title);/o:p

        Console.WriteLine(”=====================”);/o:p

        Console.WriteLine(“AssemblyQualifiedName: \“{0}\“”, type.AssemblyQualifiedName);/o:p

        Console.WriteLine(“Namespace: \“{0}\“”, type.Namespace);/o:p

        Console.WriteLine(“Name: \“{0}\“”, type.Name);/o:p

        Console.WriteLine(“FullName: \“{0}\“”, type.FullName);/o:p

        Console.WriteLine(“TypeHandle.Value: \“{0}\“”, type.TypeHandle.Value);/o:p

        Console.WriteLine(“GUID: \“{0}\“”, type.GUID);/o:p

        Console.WriteLine();/o:p

    }/o:p

 /o:p

 /o:p /o:p