Click here to Skip to main content
15,861,168 members
Articles / Programming Languages / MSIL
Article

Dynamic Assemblies using Reflection.Emit. Part II of II - Reflection.Emit

Rate me:
Please Sign up or sign in to vote.
4.24/5 (8 votes)
6 May 2007CPOL7 min read 65.7K   726   45   2
This article explains how Reflection.Emit classes can be used to dynamically generate .NET assemblies.

Read part I here.

Introduction

MSDN says:

The System.Reflection.Emit namespace contains classes that allow a compiler or tool to emit metadata and Microsoft intermediate language (MSIL) and optionally generate a PE file on disk. The primary clients of these classes are script engines and compilers.

Borrowing again from MSDN, Reflection emit provides the capabilities to:

  • Define lightweight global methods at run time, using the DynamicMethod class, and execute them using delegates.
  • Define assemblies at run time and then run them and/or save them to disk.
  • Define modules in new assemblies at run time and then run and/or save them to disk.
  • Define types in modules at run time, create instances of these types, and invoke their methods.
  • Define symbolic information for defined modules that can be used by tools such as debuggers and code profilers.

Well all of this sounds cool and way geeky, isn't it? I would probably write about all these capabilities some day, but right now my objective is to use assembly generation capabilities of Reflection.Emit to generate an assembly which is similar to one we produced using CodeDOM and dynamic code compilation in part I of this article. If you have not already read it, you can find it here.

Our generated assembly would be the same as we generated in Part I. But how do we do that? "Emitting" an assembly using Reflection.Emit classes is also a multistep process like CodeDOM. In this part also, we would directly jump to implementing the GenerateAssembly method using Reflection.Emit in a class called MSILGenerator.

Anatomy of Reflection.Emit

Reflection.Emit namespace contains several classes called as "builders" to generate various constructs like methods, types (or classes), constructors, assembly, etc. These builders provide an ILGenerator which can be used to emit required opcodes. How in the world would we know which opcodes to generate. And how do we ensure that they are correct?

As recommended by several others, I also feel the quickest way to ride this learning curve is to write simple code (an assembly with a blank class for example), build it and inspect it using dis-assembler - ILDASM. Then we can attempt to generate a similar assembly using Reflection.Emit.

Since our aim in this article is to generate a similar assembly as would be generated using CodeDOM in part I, let us have a ILDASM peek at one of the generated assemblies. It looks something like this:

Screenshot - DynamicCodeGeneration_Image2.gif

Our approach is to use Reflection.Emit to generate the same code as shown here in the same order. Section A, B & C attempt precisely the same. There is direct correlation in what is shown in the above image and the code I have written in these sections to generate the same.

One more important thing to note is that we do not need to generate each and every line of MSIL. Reflection.Emit hides several complexities from us. More on this when we generate some code below.

Any executable .NET code requires proper context of app domain, assembly, module, type and a method. The actual code which is written within the boundaries of method is the last step we would perform. The following image borrowed from Dr. Dobb's article on Generating Code at Run Time With Reflection.Emit shows the set of builder classes provided by Reflection.Emit to facilitate this process of code generation.

Image 2

A. Define Assembly

We can define assembly in the current app domain OR a new app domain. New app domain would give us control over loading and "unloading" of dynamic assemblies which we may want to do in real applications. For our example, I am using the current app domain.

C#
#region Define Assembly

AssemblyName assemblyName = new AssemblyName();
assemblyName.Name = "Derived";
AssemblyBuilder assemblyBuilder = currentDomain.DefineDynamicAssembly(
assemblyName,
AssemblyBuilderAccess.Save);
Type[] constructorParameters = new Type[] { typeof(string), typeof(GenerationMode) };
ConstructorInfo constructorInfo = 
	typeof(AssemblyLevelAttribute).GetConstructor(constructorParameters);
CustomAttributeBuilder assemblyLevelAttribute = new CustomAttributeBuilder(
constructorInfo,
new object[] { assemblyAuthorName, GenerationMode.IL });
assemblyBuilder.SetCustomAttribute(assemblyLevelAttribute);

#endregion

Reflection.Emit provides several classes called as "builders" to create different programming constructs like class, methods, constructors, properties, etc.

You might have guessed that the first thing we would need is the AssemblyBuilder class. AssemblyBuilder lets you define the target assembly. It needs an AssemblyName (this is not a simple string but an object of type AssemblyName). AssemblyBuilder constructor also asks us to specify the AccessLevel for the generated assembly (whether we want to run it/save it/both/reflection only). Since we are just interested in generating the assembly and not running it, I have chosen Save as the assembly access level.

We now need to apply the assembly level attribute to our defined assembly. We have a class called CustomAttributeBuilder exactly for this purpose. For creating an object of CustomAttributeBuilder, we need ConstructorInfo for the type defining the custom attribute. We do this here by calling the GetConstructor method of the type. This constructor info along with the object array of parameter values for the type constructor are then fed to custom attribute builder. Finally we set this assembly level custom attribute to our assembly using the SetCustomAttribute method of assembly builder.

B. Define Module

C#
#region Define module (code container)
    ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule
		( "DerivedAssembly", "DerivedAssembly.dll");
#endregion

The next step is to create a module which acts as a container of our code. This is done using ModuleBuilder.

C. Define Type (Class)

Steps:

  1. Use TypeBuilder to define the type. It is an overloaded method (6 overloads), we are using the one which takes the name of the type (Class name), attributes and the base type.
  2. Define the class level attribute using CustomAttributeBuilder like in section #A. Apply the custom attribute to type builder.
C#
#region Create the derived class with the entity subscription attribute
//First, we'll build a type
TypeBuilder typeBuilder = moduleBuilder.DefineType("Derived",
TypeAttributes.Public | TypeAttributes.BeforeFieldInit,
typeof(Base.Base));
//Let us apply the required attribute
constructorParameters = new Type[] { typeof(string) };
constructorInfo = typeof(ClassLevelAttribute).GetConstructor(constructorParameters);
CustomAttributeBuilder classLevelAttributeBuilder = 
		new CustomAttributeBuilder( constructorInfo,
    new object[] { classAuthorName });
typeBuilder.SetCustomAttribute(classLevelAttributeBuilder);
#endregion

D. Class Constructor

The next step is to define the class constructor. A default constructor is automatically supplied, however it is important to learn how to create a constructor if we need a parameterized one (or want to modify the behavior of the default constructor).

Our target MSIL code is shown below:

MSIL
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
   // Code size 7 (0x7)
   .maxstack 8
   IL_0000: ldarg.0
   IL_0001: call instance void 
	[DynamicCodeGeneration.Base]DynamicCodeGeneration.Base.Base::.ctor()
   IL_0006: ret
} // end of method Derived::.ctor

As I mentioned previously, we are not bothered about generating each line. The non-bold lines above are taken care of by the IL generator. We just need to output the opcodes for actual operations, other plumbing tasks are handled transparently.

The steps to generate the above MSIL (part in bold is what we control/generate) are:

  1. Use ConstructorBuilder class to define the constructor in the type using the DefineConstructor method of type builder.
  2. Now we are ready to start "emitting" assembly.
  3. Get the IL generator from constructor builder and emit the required opcodes shown above:
C#
#region Constructor
//Define the default constructor 
//(not required actually as default constructor is automatically supplied)
ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(
MethodAttributes.Public,
CallingConventions.Standard,
System.Type.EmptyTypes);
ILGenerator generator = constructorBuilder.GetILGenerator();

generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Call, 
	typeBuilder.BaseType.GetConstructor(System.Type.EmptyTypes));
generator.Emit(OpCodes.Ret);
#endregion

E. Overriding Method

We now need to define the overriding method in the derived class. MSIL for this:

MSIL
.method public hidebysig virtual instance void
Method() cil managed
{
    .custom instance void [DynamicCodeGeneration.CustomAttributes]
	DynamicCodeGeneration.CustomAttributes.MethodLevelAttribute::.ctor(valuetype 
	[DynamicCodeGeneration.CustomAttributes]
	DynamicCodeGeneration.CustomAttributes.ComplexityLevel,
    string) = ( 01 00 02 00 00 00 07 54 72 69 6E 69 74 79 00 00 ) // .......Trinity..
    // Code size 7 (0x7)
    .maxstack 8

    IL_0000: ldarg.0
    IL_0001: call instance void [DynamicCodeGeneration.Base]DynamicCodeGeneration.Base.Base::Method()
    IL_0006: ret

    } // end of method Derived::Method

The steps to generate the above are as follows:

  1. Create MethodBuilder object using DefineMethod
  2. Apply the method level attribute in a similar manner as in section #A and section #C.
  3. Get the ILGenerator for method and emit the required opcodes:
C#
#region Method
// Now, let's build a method and add a custom attribute to it.
MethodBuilder methodBuilder = typeBuilder.DefineMethod("Method",
      MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
      null,
      new Type[] { });

//Apply the event attribute to method
constructorParameters = new Type[] { typeof(ComplexityLevel), typeof(string) };
constructorInfo = typeof(MethodLevelAttribute).GetConstructor(constructorParameters);
CustomAttributeBuilder methodAttributeBuilder = new CustomAttributeBuilder(
    constructorInfo,
    new object[] { ComplexityLevel.SuperComplex, methodAuthorName });
methodBuilder.SetCustomAttribute(methodAttributeBuilder);

generator = methodBuilder.GetILGenerator();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Call, typeof(Base.Base).GetMethod("Method"));
generator.Emit(OpCodes.Ret);
#endregion

F. Wrap Up

After we have emitted all the required opcodes, we need to close the type. This operation puts a lid on the class and it is not possible to emit any more opcodes in the type. At this point, we can choose to save the assembly.

C#
typeBuilder.CreateType(); //Close the type
assemblyBuilder.Save("Derived.dll");
return "Derived.dll"; 

Conclusion

This concludes this two-part article. We saw how we can leverage CodeDOM & Reflection.Emit techniques to generate assemblies dynamically. We saw how to apply custom attributes at assembly, class and method. This lends us a very powerful mechanism which we can leverage in our work in so many different ways. There are so many potential uses of dynamic code generation and with .NET it is not only easy, but a lot of fun too!

Disclaimer

While writing these articles, I have freely borrowed from several resources available online. Though I have made every attempt not to leave any article in my references list, I might have inadvertently missed some citations. Please do bring to my notice such omissions and I would be glad to include the same in my article references.

References

  1. Generating Code at Run Time With Reflection.Emit by Chris Sells & Shawn Van Ness on Dr. Dobb's web portal
  2. MSDN
  3. Steve Eichert's views on dynamically generating assemblies

Further Reading

  1. CodeDOM patterns by Oman van Kloeten
  2. Tonnes of articles at Code Generation Network
  3. Introduction to Creating Dynamic Types with Reflection.Emit
  4. EmitHelper

About Proteans Software Solutions

Proteans is an outsourcing company focusing on software product development and business application development on Microsoft Technology Platform. Proteans partners with Independent Software Vendors (ISVs), System Integrators and IT teams of businesses to develop software products. Our technology focus and significant experience in software product development - designing, building, and releasing world-class, robust and scalable software products help us to reduce time-to-market, cut costs, reduce business risk and improve overall business results for our customers. Proteans expertises in development using Microsoft .NET technologies.

History

  • 6th May, 2007: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
United States United States
I work with Proteans Software Solutions. Interests include software architecture, design patterns, agile, scrum development, automated acceptance testing, books, music, travel, movies...

Comments and Discussions

 
General5 Pin
Sergey Morenko8-Mar-15 10:18
professionalSergey Morenko8-Mar-15 10:18 
QuestionQuestion about AssemblyBuilder Pin
nax9115-Jun-07 7:47
nax9115-Jun-07 7:47 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.