Pages

Saturday, October 23, 2010

DLR using Reflection.Emit (In Depth) Part 1

I love C#, I Love .NET. Do you agree ? Hmm, But I mean it :)

Well, lets put it in other words, " The more I see the framework, the more I discover in .NET". Yes, after putting my efforts with Reflection classes, I thought I could make some research on code generation. I took the CodeDom being the best alternative to generate code. Sooner or later, I found out, CodeDom actually allows you to build your assembly but it is does not allow you to dynamically compile a part of the assembly at runtime, but rather it invokes the compiler to do that. So rather than CodeDom, I thought there must be something else which fruits my needs.

Next I found out one, using Expression Trees. If you are already following me, I think you know, few days back I have already written about Expression Trees and Lamda Decomposition. So it is not a good time to recap the same. Later on, I did some research on MSIL, and found it worth learning. If you are going to grow with .NET, it would be your added advantage if you know about MSIL. Hence I started looking at the MSIL. Finally I found out a number of classes which might help you to build a Type dynamically. Let me share the entire thing with you.

Introduction

Reflection.Emit like CodeDom allows you to build your custom assembly and provides you a number of Builder classes which might be compiled during Runtime, and hence invoke DLR capabilities of C#. The library also exposes one ILGenerator which might be used later to produce the actual MSIL by putting efforts to emit Operation codes.  So finally after you write your OpCodes correctly, you could easily able to compile the type dynamically during runtime. In this post,  I would use ILDASM to see the IL generated from our own class, that I define, and later on I would try to build the same class dynamically.

What is Reflection ? 

If you are struck with Reflection, then you need to really gear yourself a bit to go further. Let me give a brief explanation of Reflection. Reflection is actually a technique to read a managed dll which is not being referenced from the application and call its types. In other words, it is a mechanism to discover the types and call its properties at runtime. Say for instance, you have an external dll which writes logger information and sends to the server. In that case, you have two options.
  1. You refer to the assembly directly and call its methods.
  2. You use Reflection to load the assembly and call it using interfaces. 
If you want to build really a decoupled architecture for your application, something like which could be plugged in later in the application, it is always better to choose the 2nd option. Let me clarify a bit more, say you want your customer to download the logging dll from your server and plugin to the application when needed. Believe me, there is no other alternative than using Reflection. Reflection classes allows you to load an external assembly to your application and call its types at run time.

To know more try Reflection Overview

What is Reflection.Emit ? 

Being a part of Reflection, Reflection.Emit namespace list you a number of classes which you can use to build your type. As I have already told you, Reflection.Emit actually provides you some Builder classes like AssemblyBuilder, ModuleBuilder, ConstructorBuilder, MethodBuilder, EventBuilder, PropertyBuilder etc. which allows you to build your IL dynamically during run time. The ILGenerator provides you the capabilities to generate your IL and place the same for a method. Generally, it is very rare that a developer need these capabilities to generate an assembly at runtime, but it is great to find these capabilities present in the framework.

Now lets see what is required to build an assembly.




Steps to generate an Assembly

Now lets jump back to identify the steps to create the assembly :

  • Create an Assembly in an Application Domain.AssemblyBuilder will help you in that.
  • Create a Module inside the Assembly
  • Create a number of Type inside a Module
  • Add Properties, Methods, Events etc inside the Type. 
  • Use ILGenerator to write inside the Properties, Methods etc.
Basically these are the common steps to create your own dynamically created Assembly.


Before we demonstrate let me show you the code which we are going to create dynamically during runtime and call its method to get the output. Please note that, I have used the most simple class with almost all the capabilities you need for building a dynamic assembly here.

public interface IBuilder
{
    float Sum(int firstnum, int secondnum);
    float Substract(int firstnum, int secondnum);
    float Multiply(int firstnum, int secondnum);
    float Divide(int firstnum, int secondnum);
}

public class Builder : IBuilder
{

    # region Event
    public delegate void BuilderDelegate(string message);

    public event BuilderDelegate InvokeMessage;

    public virtual void OnInvokeMessage(string message)
    {
        if (this.InvokeMessage != null)
            this.InvokeMessage(message);
    }

    # endregion

    # region Fields
    private int firstNum, secondNum;
    public int FirstNum
    {
        [DebuggerStepThrough()]
        get { return this.firstNum; }
        set { this.firstNum = value; }
    }

    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    public int SecondNum
    {
        get { return this.secondNum; }
        set { this.secondNum = value; }
    }

    # endregion

    # region Constructors
    public Builder(int firstnum, int secondnum)
    {
        this.FirstNum = firstnum;
        this.SecondNum = secondnum;
    }

    # endregion

    #region IBuilder Members

    public float Sum(int firstnum, int secondnum)
    {
        return firstnum + secondnum;
    }

    public float Substract(int firstnum, int secondnum)
    {
        return firstnum - secondnum;
    }

    public float Multiply(int firstnum, int secondnum)
    {
        return firstnum * secondnum;
    }

    public float Divide(int firstnum, int secondnum)
    {
        try
        {
            return firstnum / secondnum;
        }
        catch (DivideByZeroException ex)
        {
            Console.WriteLine("ZeroDivide exception : {0}", ex.Message);
            return 0;
        }
    }

    #endregion

    # region Methods

    public float GetProduct()
    {
        return this.Multiply(this.FirstNum, this.secondNum);
    }
    public override string ToString()
    {
        return string.Format("FirstNum : {0}, SecondNum : {1}", this.FirstNum, this.SecondNum);
    }

    # endregion

}

In the above class, I have actually declared an Interface which later I have implemented to produce a class Builder. The class contains few methods, events, properties etc. to allow you understand each and every flexibilities you have with it.

The Tree structure of the Assembly
After you see the code lets open ILDASM and see how the IL  looks like the picture above. Basically it contains two type .class
  1. .class interface for IBuilder
  2. .class for Builder implementing IBuilder. 
Other than that you will see another type for the Main method for my console application which you can omit for time being.

From the above figure, you can clear how the entire structure of an CLR assembly goes. The AppDomain is the root of the hierarchy which creates Assembly, then Module, and then Type. If you see the IL more thoroughly, you will understand, Delegate is also a class inherited from System.MultiCastDelegate and Struct is derived from System.ValueTypes. Each type might contain its members, and each member Method or Properties can have its OPCodes, Locals and Parameters. Locals define the local variables you define inside a method body and OpCodes are the instruction codes for the IL.

Now lets go step by step with IL to build one dynamic assembly yourself.

STEP 1. Create an Assembly Dynamically

public AssemblyBuilder GetAssemblyBuilder(string assemblyName)
{
    AssemblyName aname = new AssemblyName(assemblyName);
    AppDomain currentDomain = AppDomain.CurrentDomain; // Thread.GetDomain();
    AssemblyBuilder builder = currentDomain.DefineDynamicAssembly(aname, AssemblyBuilderAccess.Run);
    return builder;
}

To create an assembly you need to have the name of the assembly to uniquely identify the assembly. I have used AssemblyName class to name the assembly for us. AppDomain is the place where the Assembly will be created. This is very important, as application mght have issues while calling cross domain objects. To make it more simplistic, rather than creating a new AppDomain, I am using CurrentDomain where the program is running. Finally I have created an object of AssemblyBuilder, which eventually builds up the Assembly with unique name aname. The AssemblyBuilderAccess specifies the accessibility of the assembly to us. As I did, if you use Run, that means the assembly could only be run dynamically using Reflection, it cannot be saved for future use. Browse through each of the values to see the output.

Note : If you have defined a custom attribute for the assembly, you can easily go for SetCustomAttriburte to add your custom attribute to the assembly.


Few features of AssemblyBuilder (For further reading)

AssemblyBuilder allows you to define a number of features like :

  • AddResourceFile : Allows you to specify a file to be added as resource to the assembly.
  • DefineUnmanagedResource / DefineResource :  Adds one Unmanaged Resource for the assembly
  • EntryPoint/SetEntryPoint : A special subroutine / method to be defined which will be called automatically when the Assembly is invoked.
  • SetCustomAttribute : Lets you specify Attributes for the assembly. 
  • DefineDynamicModule : Defines the Module for the assembly where the actual code will contain. 

There are lot more flexibility while building the assembly. .NET put everything that we could have with the library and exposed methods to ensure we can do that from Reflection.Emit.  You can try MSDN to read more about the methods it exposes. 


STEP 2 : Create a Module

Module is a part of the object where the actual classes will remain. A module is container for all the classes we place therein. Let us create a module for us.

public ModuleBuilder GetModule(AssemblyBuilder asmBuilder)
{
    ModuleBuilder builder = asmBuilder.DefineDynamicModule("EmitMethods", "EmitMethods.dll");
    return builder;
}

Thus the method is actually expecting a ModuleName, a unique module name and the Filename to which the assembly will be exported to.

Few features of Module (for further reading)

Module exposes few methods like :
  • DefineEnum : Lets you define an Enum, it returns back an EnumBuilder. 
  • DefineType : Lets you to define a type / class. 
  • DefineManifestResource : A dll contains a binary manifest. This method lets you define the manifest for you.
  • DefinePInvokeMethod : Allows you to define a PInvoke method (COM) for the assembly.

STEP 3 : Create a Type 

This is the main thing. To create a class, structure, delegate etc you need to define a TypeBuilder.  Now from here onwards I will look into the actual IL generated using ILDASM and then produce the same output for you.

public TypeBuilder GetType(ModuleBuilder modBuilder, string className)
{
    TypeBuilder builder = modBuilder.DefineType(className, TypeAttributes.Public);
    return builder;
}

public TypeBuilder GetType(ModuleBuilder modBuilder, string className, params string[] genericparameters)
{
    TypeBuilder builder = modBuilder.DefineType(className, TypeAttributes.Public);
    GenericTypeParameterBuilder[] genBuilders = builder.DefineGenericParameters(genericparameters);

    foreach (GenericTypeParameterBuilder genBuilder in genBuilders) // We take each generic type T : class, new()
    {
        genBuilder.SetGenericParameterAttributes(GenericParameterAttributes.ReferenceTypeConstraint | GenericParameterAttributes.DefaultConstructorConstraint);
        //genBuilder.SetInterfaceConstraints(interfaces);
    }

    return builder;
}

The above GetType method has two overloads. As you can see the first one is simple one where I have just specified the name of the class and the ModuleBuilder and the method returns the TypeBuilder.

In the second overload, I have put an additional param array of string which defines each Generic type for the class. GenericTypeParameterBuilder allows you to define the GenericTypeParameter. Once you define the GenericTypeParameters and set its constraint attributes, you can the builder.

Few features of TypeBuilder (for further reading)

Compared to classes, TypeBuilder allows you to define full fledged structure with all the options you have. Some of them are :
  • DefineField / DefineMethod / DefineProperties / DefineEvent : You might use these methods to generate class members.
  • DefineMethodOverride : Allows you to override an existing method when the Type is inherited from another base type
  • DefineConstructor / DefineDefaultConstructor : Specifies the constructor for the current type.
  • AddInterfaceImplementation : Allows you to implement the current type from another interface.
 STEP 4 : Create Method

Method are the building block of any program. We will define a number of Methods to clear up the concepts on how easily you could build a method from IL. For the time being, lets we create a dynamic method using MethodBuilder.

public MethodBuilder GetMethod(TypeBuilder typBuilder, string methodName)
{
    MethodBuilder builder = typBuilder.DefineMethod(methodName, MethodAttributes.Public | MethodAttributes.HideBySig);
    return builder;
}
public MethodBuilder GetMethod(TypeBuilder typBuilder, string methodName, Type returnType, params Type[] parameterTypes)
{
    MethodBuilder builder = typBuilder.DefineMethod(methodName, MethodAttributes.Public | MethodAttributes.HideBySig, CallingConventions.HasThis, returnType, parameterTypes);
    return builder;
}

public MethodBuilder GetMethod(TypeBuilder typBuilder, string methodName, Type returnType, string[] genericParameters, params Type[] parameterTypes)
{
    MethodBuilder builder = typBuilder.DefineMethod(methodName, MethodAttributes.Public | MethodAttributes.HideBySig, CallingConventions.HasThis, returnType, parameterTypes);

    GenericTypeParameterBuilder[] genBuilders = builder.DefineGenericParameters(genericParameters);

    foreach (GenericTypeParameterBuilder genBuilder in genBuilders) // We take each generic type T : class, new()
    {
        genBuilder.SetGenericParameterAttributes(GenericParameterAttributes.ReferenceTypeConstraint | GenericParameterAttributes.DefaultConstructorConstraint);
        //genBuilder.SetInterfaceConstraints(interfaces);
    }
    return builder;
}

So the above methods will return you the MethodBuilder which lets you to define your IL code.  You can see I have specified 3 overloads for this. The overloads allows you to put parameters and also to put Generic Type parameters for the methods.

Now, after building your type you need to create Locals (local variables) and use  OpCode instructions.

ILGenerator


To define your OpCodes you will need ILGenerator. ILGenerator allows you to Emit IL for your method body and hence create the exact same method.

public void CreateMethod()
{
    AppDomain currentDomain = AppDomain.CurrentDomain;
    AssemblyBuilder asmbuilder = this.GetAssemblyBuilder("MyAssembly");
    ModuleBuilder mbuilder = this.GetModule(asmbuilder);
    TypeBuilder tbuilder = this.GetTypeBuilder(mbuilder, "MyClass");

    Type[] tparams = { typeof(System.Int32), typeof(System.Int32) };
    MethodBuilder methodSum = this.GetMethod(tbuilder, "Sum", typeof(System.Single), tparams);
    ILGenerator generator = methodSum.GetILGenerator();
    generator.Emit(OpCodes.Ldarg_0);
    generator.Emit(OpCodes.Ldarg_1);
    generator.Emit(OpCodes.Add_Ovf);
    generator.Emit(OpCodes.Stloc_0);
    generator.Emit(OpCodes.Br_S);
    generator.Emit(OpCodes.Ldloc_0);
    generator.Emit(OpCodes.Ret);

    Type thistype = tbuilder.CreateType();
}

So finally, I have tried to mimic the IL generated for Sum of two numbers and produced the Type thisType for us. You can use this Type to invoke its members using Reflection. Isn't it cool. Hmm, you will make it more clear very recently.

...

As the topic is getting more and more interesting and more and more complicated, lets divide the article in 2 parts. Lets end the article here. In the next section, I will put more IL and generate Events, Delegates, Methods, etc.  I will give you the second link as soon as I am done.  Stay tune.

Thank you for reading my article.

1 comment:

Please make sure that the question you ask is somehow related to the post you choose. Otherwise you post your general question in Forum section.