TypeName Grammar

I was about to post another blog regarding to tokens and handles and generics, but our Reflection tester Haibo suggested me to talk a little about the TypeName Grammar that we added in Whidbey because a lot of external bugs are being reported on this issue. 

 

Let's start from a bug:

Type.GetType("System.Drawing.Font") returns null

 

We can get the type of System.Object through Type.GetType(“System.Object”) but why we cannot get the type of System.Drawing.Fone? It simply because all we know is to look up your executing assembly and mscorlib for the types specified with simple name and we couldn't find the type because System.Drawing.Font is in System.Drawing.dll.

You will have to pass in the type name plus the assembly name in order for us to load the assembly then look for the type in that asssembly.

 

To make your program robust and being able to always get the Type back, you should be using the type's AssemblyFullyQualifiedName to get the type back. The easiest way to know a the AssemblyFullQualifiedName is to call t.AssemblyFullyQualifiedName.

For example, System.Drawing.Font  type’s AFQN is:

System.Drawing.Font, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a

 

The rule behind it is:

t.FullName + “,” + asm.FullName = t.AFQN

 

For Generic Types, things become more complicated. The simplest way is still to call t. AssemblyFullyQualifiedName to see what is the right string to be used in Type.GetType.

 

A simple Generic Type G<Object, String> ’s AQFN may look like this:

G`2[[System.Object, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null

 

It looks pretty complicated but to break it up, it is reasonable.

We cannot use single “[“ because then we will not know when is the start of a new Generic Parameter (they are all commas).

Calling

Type.GetType(“G`2[[System.Object],[System.String]]”) will return you the type because in this case, G, System.Object and System.String all reside in the default look up scope. If any of these types are not in the excuting assembly or mscorlib, then you will have to specify the AQFN of that type.

In our case, all the following names will retrieve the type back for you.

 

G`2[[System.Object, mscorlib],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]

G`2[[System.Object, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String]]

G`2[[System.Object, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]

 

So what should be the G<Object, String>'s full name? According to the rule above, it should be:

G`2[[System.Object, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]

 

This fullname does contain some assembly infomation which seem to be a little strange to some users. But that assembly infomation is about the Generic Arguments and not about the type itself. We need them because Generic Arguments in live in a different assembly as the Generic Type Definition.

 

If you want to use Type.GetType to get back G<object>, the string to use is G[[System.Object]]. Again double "[" here.

 

If you want to automatically compose/parse these type names on large scales without having the type first, we provided a set of TypeNameBuilder and TypeNameParser APIs through mscoree.tlb. You can consume them either in unmanaged code or from managed side through interop.

 

Here is a small sample of using the API from managed side:

 

Tlbimp mscoree.tlb /out=mscoree_me.dll

using System;

using System.Reflection;

using System.Runtime.InteropServices;

using mscoree_mm;

 

public class TypeNameBuilderAPITest

{

            public static void Main()

            {

                       

                        ITypeNameFactory tf = new TypeNameFactoryClass();

                        ITypeNameBuilder tb = tf.GetTypeNameBuilder();

 

                        tb.Clear();

                        tb.AddName("G");

                        tb.OpenGenericArguments();

                        tb.OpenGenericArgument();

                        tb.AddName("T");

                        tb.AddAssemblySpec("Asm");

                        tb.CloseGenericArgument();

                        tb.CloseGenericArguments();

                        tb.AddSzArray();

                        Console.WriteLine(tb.ToString());

                       

                        uint Epos = 0; // error position

                        ITypeName tn = tf.ParseTypeName(tb.ToString(), out Epos);

                        Console.WriteLine(tn.GetTypeArgumentCount());

}

}

 

Output:

G[[T, Asm]][]

1

 

The last note is that, for Type.GetType(String typeName) if the typeName is correct in the grammar but we cannot find the type, we will return null on this API. If the type name grammar is incorrect, ArgumentException will be thrown.