A Tool (attached) to help figure out how to emit a type

After being in Exchange for almost a year, I tend to forget the how to run Reflection.Emit that I was once so familiared with.  Occasionally, I would receive emails from this blog asking how to emit this, how to emit that. Before, the solutions came directly from my memory, but now I need some help. The way for me to figure it out is to compile the thing I want to emit into a binary file and run it under my little tool AssemblyRoundTrip.exe with verbose output.

I wrote the tool for testing purpose. I ran every assembly I can get under that tool and it will figure out for me whether we can or cannot emit the assembly.

Now, I found it really helpful for myself to figure out things should be done. I am posting the tool here and hopefully it will help you to work out some generic mystery.

Here is an example, if I want to define Test<T>:List<T> type, I wrote it in C# and compile it. Then I ran

AssemblyRoundTrip.exe repro.dll /verbose

and here is the output:

new Asm Name is repro_Emit
Security Attribute number :0
setting ca:[System.Runtime.CompilerServices.CompilationRelaxationsAttribute((Int
32)8)]
setting ca:[System.Runtime.CompilerServices.RuntimeCompatibilityAttribute(WrapNo
nExceptionThrows = True)]
new module name is repro_Emit.dll
Total Types:1
m_modBuilder.DefineType M`1     <---- Define the type
Adding M`1[T] to the create list
Define Generic Parameters on M`1  <---- Define the type parameter
Adding Generic Parameter M`1[T]::T on Type M`1 to the create type list
Found a match for :M`1::T   <--- this step is a preparation for the type below. We are binding List`1[T] (the type below) to T
Set Parent for M`1 System.Collections.Generic.List`1[T]   <--- set the paraent
Found a match for :M`1::T
Set Interface for M`1 System.Collections.Generic.IList`1[T]
Found a match for :M`1::T
Set Interface for M`1 System.Collections.Generic.ICollection`1[T]
Found a match for :M`1::T
Set Interface for M`1 System.Collections.Generic.IEnumerable`1[T]
Set Interface for M`1 System.Collections.IEnumerable
Set Interface for M`1 System.Collections.IList
Set Interface for M`1 System.Collections.ICollection
+++ Create Method : M`1[T]::.ctor
Security Attribute for type :M`1[T] is 0
Adding M`1[T] to bake type list
+++ Created Type : M`1

 

So I translate the verbose output into the following code:

using System;

using System.Reflection;

using System.Collections.Generic;

using System.Reflection.Emit;

 

public class Test

{

            public static void Main()

            {

            string typename = "ListProxy";

            AssemblyName asmname = new AssemblyName("mytest");

                AssemblyBuilder asmbuild = AppDomain.CurrentDomain.DefineDynamicAssembly(asmname, AssemblyBuilderAccess.RunAndSave);

                ModuleBuilder moduleBuilder = asmbuild.DefineDynamicModule("mytest", "mytest.dll");

            TypeAttributes typeAttributes = TypeAttributes.Public | TypeAttributes.Class;

           

            TypeBuilder typeBuilder = moduleBuilder.DefineType(typename, typeAttributes);

                GenericTypeParameterBuilder[] tpbs = typeBuilder.DefineGenericParameters("T"); // defines a generic type definition

                typeBuilder.MakeGenericType(typeof(List<>).GetGenericArguments()[0]);  

                typeBuilder.SetParent(typeof(List<>).MakeGenericType(tpbs[0])); // bind the correct base type

                typeBuilder.CreateType();

                asmbuild.Save("mytest");

            }

}

I attached the tool as it is and I am unlikely to support this tool. I could send the source code upon request but the source code is a little confusing since it is a test tool so it tend to has many extra stuff that we are interested to test but not related to the main purpose.

If you set EXT_ROOT to your runtime install path, the tool will run peverify for you at the end to verify the generated assembly.

You will find there is another AssemblyPrinter.exe, it is a reflection verstion of ildasm outputing text file. The only difference is that AssemblyPrinter.exe prints out members in deterministic order. So if you want to compare the two assemblies, AssemblyPrinter.exe is a good tool for that. ildasm.exe output on two assemblies compiled from the same source tend to be difference because the runtime doesn't grarantee the ordering of members to be the same. (This should remind you not to use GetMethods[1] or GetCustomAttributes[2] since they tend to change!)

ILReader.rar