Pages

Sunday, October 24, 2010

DLR using Reflection.Emit (In Depth) Part 2

In my previous post, I was discussing how you could create your own assembly at runtime or rather how you could compile an assembly type dynamically using Reflection.Emit.  In this post I will take it further by giving away a number of examples for your better understanding how to build your own custom types dynamically during runtime. I will also try to cover up a portion of MSIL concepts so that you could unleash the power of MSIL easily in your application.

The Basics

To build a dynamic type, the most important thing that we have discussed already are Builder classes. The BCL exposes a number of Builder classes that enables us to generate MSIL code dynamically during runtime and hence you can compile the same to produce the output.


From the above figure I have put some of the most important Builder classes marked in Red. But ultimately, you need instructions to run for your application. To write your IL Expression, Reflection.Emit provides a class call ILGenerator. ILGenerator (marked in blue) enables you to write your IL for a method or property. OpCodes are Operation code that determined Computer instructions. So while writing your instructions you need to pass your OpCodes and generate the instruction set for the Method.

Now going further with our example, let me demonstrate the code one by one so that you could easily build your own Code generator. 

Implement IBuilder for your Assembly

Lets move to the actual implementation of IBuilder interface. As per our discussion, IBuilder contains 4 members, Sum, Divide, Multiply & substract.

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);
}

Download Sample Application - 66 KB



So, as I am new to IL, lets use ILDASM to see what exactly the IL looks like and hence we will try to implement the same for our assembly.

Hmm... After I build and Open ILDASM to disassemble my assembly the IL it produces looks like :

.class interface public abstract auto ansi EmitMethodSample.IBuilder
{
    .method public hidebysig newslot abstract virtual 
                    instance float32  Divide(int32 firstnum,
                        int32 secondnum) cil managed
    {
    }
    .method public hidebysig newslot abstract virtual 
                    instance float32  Sum(int32 firstnum,
                    int32 secondnum) cil managed
    {
    }
    .method public hidebysig newslot abstract virtual 
                    instance float32  Multiply(int32 firstnum,
                        int32 secondnum) cil managed
    {
    }
    .method public hidebysig newslot abstract virtual 
                    instance float32  Substract(int32 firstnum,
                        int32 secondnum) cil managed
    {
    }
                
} 

Let me explain the IL a bit.

  • In MSIL any type is defined with .class, hence our Type IBuilder gets one .class for it. 
  • interface, abstract keyword identifies the Type to be abstract and hence you cannot create object of the same. 
  • Auto specifies the LPSTR(Long Pointer to string) interpreted automatically.
  • Ansi specifies the LPSTR(Long Pointer to String) interpreted as ANSI.
  • Methods in IBuilder is identified using .method keyword. 
  • Because of being member of an interface, the method are automatically set as abstract virtual
  • The methods being nonstatic it gets instance keyword in MSIL definition.
  • hidebysig specifies the method can be hidden by both name and signature. Any normal method you define gets this flexibility in .NET. 
  • newslot makes the member to get a slot in vtable. (A vtable is the memory area for the whole object. So whenever an object is created, a vtable is created each for each object and any object created within it gets an entry of vtable. The first member being the hidden pointer can be used to find members of the vtable)
  • cil managed is used to determine that the method is implemented in managed environment. 
Now as you now understand the IL generated by the Interface, its time to get through to create the Type IBuilder. Lets see the code :

private Type CreateIBuilder(ModuleBuilder mbuilder)
{

    TypeBuilder tbuilder = mbuilder.DefineType("IBuilder", TypeAttributes.Interface | 
        TypeAttributes.Public | 
        TypeAttributes.Abstract | 
        TypeAttributes.AutoClass | 
        TypeAttributes.AnsiClass);

    //Define Divide
    Type[] tparams = { typeof(System.Int32), typeof(System.Int32) };
    MethodBuilder metDivide = tbuilder.DefineMethod("Divide", MethodAttributes.Public | 
        MethodAttributes.Abstract | 
        MethodAttributes.Virtual |
        MethodAttributes.HideBySig | 
        MethodAttributes.NewSlot, 
        CallingConventions.HasThis, 
        typeof(System.Single), tparams);
    metDivide.SetImplementationFlags(MethodImplAttributes.Managed);

    MethodBuilder metSum = tbuilder.DefineMethod("Sum", MethodAttributes.Public |
        MethodAttributes.Abstract |
        MethodAttributes.Virtual |
        MethodAttributes.HideBySig |
        MethodAttributes.NewSlot,
        CallingConventions.HasThis,
        typeof(System.Single), tparams);
    metSum.SetImplementationFlags(MethodImplAttributes.Managed);

    MethodBuilder metMultiply = tbuilder.DefineMethod("Multiply", MethodAttributes.Public |
        MethodAttributes.Abstract |
        MethodAttributes.Virtual |
        MethodAttributes.HideBySig |
        MethodAttributes.NewSlot,
        CallingConventions.HasThis,
        typeof(System.Single), tparams);
    metMultiply.SetImplementationFlags(MethodImplAttributes.Managed);

    MethodBuilder metSubstract = tbuilder.DefineMethod("Substract", MethodAttributes.Public |
        MethodAttributes.Abstract |
        MethodAttributes.Virtual |
        MethodAttributes.HideBySig |
        MethodAttributes.NewSlot,
        CallingConventions.HasThis,
        typeof(System.Single), tparams);
    metSubstract.SetImplementationFlags(MethodImplAttributes.Managed);

    Type tIBuilder = tbuilder.CreateType();


    return tIBuilder;
}

In the above code we first create the TypeBuilder from ModuleBuilder.DefineType. You should note, I have added the TypeAttributes in the same way as it was in MSIL. After we create the TypeBuilder, we can next add up the methods. The DefineMethod method helps in building the methods as we define the MethodAttributes correctly.  CallingConvensions.HasThis will make the method as instance method.. We also need to mention the ReturnType and argument types specifically. In this case I have specified the ReturnType as System.Single(float) and arguments as integers for our code.  It should be noted, we need to use SetImplementationFlags to specify the methods to be cil managed.

Finally we call CreateType of TypeBuilder to get the actual type.

Creating the Builder Class

Hmm, now its time to go final workaround with this. First I will create the basic class with methods only required to implement the interface IBuilder, later on we will add the delegate, event, a new method, a static method etc.

To create the basic Builder class, the first thing that we need is a Constructor. But as our constructor also adds few lines to initialize properties FirstNum and SecondNum, let me define them first.

1. Implementing the Type 

Lets first work on implementation of the TypeBuilder and then go on Fields and Properties

.class public auto ansi beforefieldinit EmitMethodSample.Builder
    extends [mscorlib]System.Object
    implements EmitMethodSample.IBuilder
{
}

So basically the class extends System.Object (as for any class which does not inherit form other class) and in our case it implements IBuilder.

I guess,  building the type using TypeBuilder should be easy for you, let me build it again for you :

Type[] interfaces = { parentBuilder };
TypeBuilder tbuilder = mbuilder.DefineType("Builder", TypeAttributes.Public |
    TypeAttributes.AutoClass |
    TypeAttributes.AnsiClass |
    TypeAttributes.BeforeFieldInit,
    typeof(System.Object),
    interfaces);

So here in the code I have implemented the Builder  from parentBuilder  which is the Type object of  IBuilder interface. If you focus on the code, you will see I have specified BeforeFieldInit for the type, which means you can call the static members without initializing the object.   I have also implemented the Type from System.Object according to IL.

2. Implementing the Field & Properties

As our type is ready now, let us add some field and properties on the Type.
The actual code that adds up the Fields and Properties looks like
private int firstNum, secondNum;
public int FirstNum
{
    get { return this.firstNum; }
    set { this.firstNum = value; }
}

public int SecondNum
{
    get { return this.secondNum; }
    set { this.secondNum = value; }
}

Lets see the IL for the property FirstNum:

.field private int32 firstNum
.property instance int32 FirstNum()
{
    .set instance void EmitMethodSample.Builder::set_FirstNum(int32)
    .get instance int32 EmitMethodSample.Builder::get_FirstNum()
}
.method public hidebysig specialname instance int32 
        get_FirstNum() cil managed
{
    .custom instance void [mscorlib]System.Diagnostics.DebuggerStepThroughAttribute::.ctor() = ( 01 00 00 00 ) 
    // Code size       12 (0xc)
    .maxstack  1
    .locals init (int32 V_0)
    IL_0000:  nop
    IL_0001:  ldarg.0
    IL_0002:  ldfld      int32 EmitMethodSample.Builder::firstNum
    IL_0007:  stloc.0
    IL_0008:  br.s       IL_000a
    IL_000a:  ldloc.0
    IL_000b:  ret
}
.method public hidebysig specialname instance void 
        set_FirstNum(int32 'value') cil managed
{
    // Code size       9 (0x9)
    .maxstack  8
    IL_0000:  nop
    IL_0001:  ldarg.0
    IL_0002:  ldarg.1
    IL_0003:  stfld      int32 EmitMethodSample.Builder::firstNum
    IL_0008:  ret
}

So considering the IL, property seems to be a wrapper for two methods one with get_PropertyName and another with set_PropertyName where get_PropertyName returns the value and set_PropertyName sets the value.

FieldBuilder fFirst = tbuilder.DefineField("firstNum", typeof(System.Int32), FieldAttributes.Private);
PropertyBuilder pFirst = tbuilder.DefineProperty("FirstNum", PropertyAttributes.HasDefault, typeof(System.Int32), null);
//Getter
MethodBuilder mFirstGet = tbuilder.DefineMethod("get_FirstNum", MethodAttributes.Public | 
    MethodAttributes.SpecialName | 
    MethodAttributes.HideBySig, typeof(System.Int32), Type.EmptyTypes);
ILGenerator firstGetIL = mFirstGet.GetILGenerator();

firstGetIL.Emit(OpCodes.Ldarg_0);
firstGetIL.Emit(OpCodes.Ldfld, fFirst);
firstGetIL.Emit(OpCodes.Ret);

//Setter
MethodBuilder mFirstSet = tbuilder.DefineMethod("set_FirstNum", MethodAttributes.Public |
    MethodAttributes.SpecialName |
    MethodAttributes.HideBySig, null, new Type[] { typeof(System.Int32) });

ILGenerator firstSetIL = mFirstSet.GetILGenerator();

firstSetIL.Emit(OpCodes.Ldarg_0);
firstSetIL.Emit(OpCodes.Ldarg_1);
firstSetIL.Emit(OpCodes.Stfld, fFirst);
firstSetIL.Emit(OpCodes.Ret);

pFirst.SetGetMethod(mFirstGet);
pFirst.SetSetMethod(mFirstSet);

Ohh, its huge.... .Yes, let me explain. First I have added a Field firstNum which is a numeric private variable. A FieldBuilder helps you to add a field into the IL. To define a property you need to first define the property itself and then you have to define two methods one for Getter and one for Setter such that the Getter returns System.Int32 and the Setter takes System.Int32 as argument.

The OpCodes provide the entire expression set. ldarg loads the argument and Ldfld and Stfld loads and sets the fields into the field fFirst.

A Nice thing to talk
So you must understand now, a property actually reserves the methods with get_Property and set_Property with the same signature.  Say for instance you define a class with the following :
private string first;
public string First { get { return this.first; } set { this.first = value; } }

public string get_First()
{
    return this.first;
}
public void set_First(string value)
{
    this.first = value;
}

The class will not compile, as get_First and set_First is already reserved and the compiler throws warning doing this.


Isn't it interesting to know ?

3. Implementing the Constructor


If your class does not define a constructor in it, the C# compiler automatically puts in a default constructor for you. How it does? Actually for any class, the default constructor for System.Object is automatically inherited to the object and hence you do not need to define a default constructor in such case. It will be written in IL only when you define the default constructor explicitly.

In our case, I have explicit declaration of a parametrized constructor, as it is good to show you the code for that.

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

To do this let me quickly show you how the constructor looks like in terms of IL.

.method public hidebysig specialname rtspecialname 
        instance void  .ctor(int32 firstnum,
                int32 secondnum) cil managed
{
    // Code size       26 (0x1a)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
    IL_0006:  nop
    IL_0007:  nop
    IL_0008:  ldarg.0
    IL_0009:  ldarg.1
    IL_000a:  call       instance void EmitMethodSample.Builder::set_FirstNum(int32)
    IL_000f:  nop
    IL_0010:  ldarg.0
    IL_0011:  ldarg.2
    IL_0012:  call       instance void EmitMethodSample.Builder::set_SecondNum(int32)
    IL_0017:  nop
    IL_0018:  nop
    IL_0019:  ret
}

To build a constructor, we need to create object of ConstructorBuilder which you can get from DefineConstructor method of TypeBuilder. If you see the IL, you can see, the IL actually calls the constructor of System.Object first. This is needed, as any object internally inherited from Base System.Object.

The setFirstNum and set_SecondNum is called from the IL to set the values for FirstNum and SecondNum of the class.

Type[] parameters = { typeof(System.Int32), typeof(System.Int32) };
ConstructorBuilder cBuilder = tbuilder.DefineConstructor(MethodAttributes.Public | 
    MethodAttributes.HideBySig | 
    MethodAttributes.SpecialName | 
    MethodAttributes.RTSpecialName, 
    CallingConventions.Standard, 
    parameters);

ConstructorInfo conObj = typeof(object).GetConstructor(new Type[0]);

ILGenerator cil = cBuilder.GetILGenerator();
cil.Emit(OpCodes.Ldarg_0);
cil.Emit(OpCodes.Call, conObj);
cil.Emit(OpCodes.Nop);
cil.Emit(OpCodes.Nop);
cil.Emit(OpCodes.Ldarg_0);
cil.Emit(OpCodes.Ldarg_1);
cil.Emit(OpCodes.Call, mFirstSet);
cil.Emit(OpCodes.Nop);
cil.Emit(OpCodes.Ldarg_0);
cil.Emit(OpCodes.Ldarg_1);
cil.Emit(OpCodes.Call, mSecondSet);
cil.Emit(OpCodes.Nop);
cil.Emit(OpCodes.Nop);
cil.Emit(OpCodes.Ret);

The SpecialName in MethodAttribute for Constructor lets the method to be special to CLR. Hence the name of the method ctor makes it a constructor of the class.

To call the constructor of System.Object, we need to fetch the constructor of the object. I have used Reflection to get ConstructorInfo from Type and passed in to Call OpCode.We emit the code as specified in the IL and hence the constructor will be generated.

One interesting thing to remember about Reflection.Emit is that, it internally sends a hidden object to every method it calls. This is the implicit object call which we identify as "this" in C# or "Me" in Vb. Thus when we call Ldarg_0 for OpCodes, we are actually mentioning to the implicit object passed into the constructor as first argument.  So any parameter we specify starts with Index 1.
The only difference between the Constructor and a normal method is that, a Constructor does not return a value. In CLR, a method actually returns the top element in the stack immediately if OpCodes.Ret is received. So if your stack loads a value into stack before calling Ret, you will get "Invalid Program" exception when you create object of the type. So in such cases Nop should be invoked before calling Ret to consume a processing cycle.

Now that we have defined the constructor let me move forward to define the methods.

4. Implementing the Methods from IBuilder

Now as our constructor is ready, its time to implement the IBuilder object and define the Methods for us.  As we are going through with code, I think it must be clear to you how to build your own custom objects. Lets try out the Divide method of IBuilder and implement the same for us.

Lets revisit what I have implemented for Divide.
public float Divide(int firstnum, int secondnum)
{
    try
    {
        return firstnum / secondnum;
    }
    catch (DivideByZeroException ex)
    {
        Console.WriteLine("ZeroDivide exception : {0}", ex.Message);
        return 0;
    }
}

Now considering ILDASM, the IL for Divide method looks like :
.method public hidebysig newslot virtual final 
            instance float32  Divide(int32 firstnum,
                        int32 secondnum) cil managed
    {
        // Code size       39 (0x27)
        .maxstack  2
        .locals init (class [mscorlib]System.DivideByZeroException V_0,
                float32 V_1)
        IL_0000:  nop
        .try
        {
        IL_0001:  nop
        IL_0002:  ldarg.1
        IL_0003:  ldarg.2
        IL_0004:  div
        IL_0005:  conv.r4
        IL_0006:  stloc.1
        IL_0007:  leave.s    IL_0024
        }  // end .try
        catch [mscorlib]System.DivideByZeroException 
        {
        IL_0009:  stloc.0
        IL_000a:  nop
        IL_000b:  ldstr      "ZeroDivide exception : {0}"
        IL_0010:  ldloc.0
        IL_0011:  callvirt   instance string [mscorlib]System.Exception::get_Message()
        IL_0016:  call       void [mscorlib]System.Console::WriteLine(string,
                                                                        object)
        IL_001b:  nop
        IL_001c:  ldc.r4     0.0
        IL_0021:  stloc.1
        IL_0022:  leave.s    IL_0024
        }  // end handler
        IL_0024:  nop
        IL_0025:  ldloc.1
        IL_0026:  ret
    }
So the only difference that you can see is the Try / Catch block in IL. So lets see how I implement the Try/Catch block.
MethodBuilder mDivide = tbuilder.DefineMethod("Divide", MethodAttributes.Public |
    MethodAttributes.HideBySig |
    MethodAttributes.NewSlot |
    MethodAttributes.Virtual |
    MethodAttributes.Final,
    CallingConventions.Standard,
    typeof(System.Single),
    new Type[] { typeof(System.Int32), typeof(System.Int32) });
mDivide.SetImplementationFlags(MethodImplAttributes.Managed);
ILGenerator dil = mDivide.GetILGenerator();

dil.Emit(OpCodes.Nop);
Label lblTry = dil.BeginExceptionBlock();

dil.Emit(OpCodes.Nop);
dil.Emit(OpCodes.Ldarg_1);
dil.Emit(OpCodes.Ldarg_2);
dil.Emit(OpCodes.Div);
dil.Emit(OpCodes.Conv_R4); // Converts to Float32
dil.Emit(OpCodes.Stloc_1);
dil.Emit(OpCodes.Leave, lblTry);

dil.BeginCatchBlock(typeof(DivideByZeroException));
dil.Emit(OpCodes.Stloc_0);
dil.Emit(OpCodes.Nop);
dil.Emit(OpCodes.Ldstr, "ZeroDivide exception : {0}");
dil.Emit(OpCodes.Ldloc_0);
MethodInfo minfo = typeof(DivideByZeroException).GetMethod("get_Message");
dil.Emit(OpCodes.Callvirt, minfo);
MethodInfo wl = typeof(System.Console).GetMethod("WriteLine", new Type[] { typeof(string), typeof(object) });
dil.Emit(OpCodes.Call, wl);
dil.Emit(OpCodes.Nop);
dil.Emit(OpCodes.Ldc_R4, 0.0);
dil.Emit(OpCodes.Stloc_1);
dil.Emit(OpCodes.Leave_S, lblTry);

dil.EndExceptionBlock();
dil.Emit(OpCodes.Nop);
dil.Emit(OpCodes.Ldloc_1);
dil.Emit(OpCodes.Ret);

To open a Try block in IL, you need to use BeginExceptionBlock. Be sure to store the Label so that you could move to specific IL instruction code whenever required.  Now Before starting the Catch block which is notified using BeginCatchBlock we need to Leave the Try block using OpCodes.Leave with the LabelName. This will ensure that the application maintains Scope.
You can see, we could have more than one catch block, and each of them will be identified by the Type passed into BeginCatchBlock. Hence, we just Load the string inside Catch block and call the Method  WriteLine of Console to show the string.  Finally, again before calling EndExceptionBlock, we leave the Try/catch block.

You should note, whenever you are about to call a method, you need to use a MethodInfo object.

Now lets create another method for Sum :

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

The IL would look like :

.method public hidebysig newslot virtual final 
    instance float32  Sum(int32 firstnum,
                    int32 secondnum) cil managed
{
    // Code size       10 (0xa)
    .maxstack  2
    .locals init (float32 V_0)
    IL_0000:  nop
    IL_0001:  ldarg.1
    IL_0002:  ldarg.2
    IL_0003:  add
    IL_0004:  conv.r4
    IL_0005:  stloc.0
    IL_0006:  br.s       IL_0008
    IL_0008:  ldloc.0
    IL_0009:  ret
}

Hence the code will look like :
MethodBuilder mSum = tbuilder.DefineMethod("Sum", MethodAttributes.Public |
    MethodAttributes.HideBySig |
    MethodAttributes.NewSlot |
    MethodAttributes.Virtual |
    MethodAttributes.Final,
    CallingConventions.Standard,
    typeof(System.Single),
    new Type[] { typeof(System.Int32), typeof(System.Int32) });
mSum.SetImplementationFlags(MethodImplAttributes.Managed);
ILGenerator sil = mSum.GetILGenerator();
Label endofmethodSum = sil.DefineLabel();
sil.Emit(OpCodes.Nop);
sil.Emit(OpCodes.Ldarg_1);
sil.Emit(OpCodes.Ldarg_2);
sil.Emit(OpCodes.Add);
sil.Emit(OpCodes.Conv_R4); // Converts to Float32
sil.Emit(OpCodes.Stloc_0);
sil.Emit(OpCodes.Br_S,endofmethodSum);
sil.Emit(OpCodes.Ldloc_0);
sil.MarkLabel(endofmethodSum);
sil.Emit(OpCodes.Ret);

Here the IL requires to more from one line to another. If you look back the IL, the instruction line IL0006 puts a Go To statement to IL0008. To do this, I have used DefineLabel. The DefineLabel of ILGenerator actually gives you a Label object which you can later mark using MarkLabel. Hence, in our case I have marked IL0008 as endofmethodSum.

I think you can now create the IL for other methods easily. Lets now run the code to check whether the IL works correctly or not :

Wrapping up the whole thing

Now as we have implemented all the methods, let me check whether I have correctly produced the IL or not.

//Step 1 : Create the Assembly
AssemblyBuilder asmBuilder = this.GetAssemblyBuilder("MyBuilder");

//Step 2: Add A Module to the Assembly
ModuleBuilder mbuilder = this.GetModule(asmBuilder);

//Step 3: Add the Type IBuilder
Type iBuilder = this.CreateIBuilder(mbuilder);

//Step 4 : Implement IBuilder to create Builder 
Type Builder = this.CreateBuilderImpl(mbuilder, iBuilder);

dynamic variable = Activator.CreateInstance(Builder, new object[] { 20, 10 });
float result = variable.Sum(30, 40);
Console.WriteLine("Result  for Sum(30, 40) : {0}", result);
result = variable.Substract(50, 25);
Console.WriteLine("Result  for Substract(50, 25) : {0}", result);
result = variable.Multiply(3, 5);
Console.WriteLine("Result  for Multiply(3, 5) : {0}", result);
result = variable.Divide(30, 5);
Console.WriteLine("Result  for Divide(30, 5) : {0}", result);

You should note, I have used dynamic to avoid unnecessary usage of Reflection classes again.

So it looks fine and after I compile I get output like
So the IL generated works great for us.

You can also save the IL as Assembly using
asmBuilder.Save("MyBuilder.dll");

Building a Delegate

Now as we move forward, It would be a good idea to show you how to build a Delegate for a class. Building a delegate differs from others. Say for instance we are going to define a simple Delegate method :

public delegate void BuilderDelegate(string message);

Being the most simple delegate declaration, if you see the IL definition for the delegate it looks like :
.class auto ansi sealed nested public BuilderDelegate
    extends [mscorlib]System.MulticastDelegate
{
    .method public hidebysig specialname rtspecialname 
        instance void  .ctor(object 'object',
                        native int 'method') runtime managed
    {
    }
    .method public hidebysig newslot virtual 
        instance class [mscorlib]System.IAsyncResult 
        BeginInvoke(string message,
            class [mscorlib]System.AsyncCallback callback,
            object 'object') runtime managed
    {
    }
    .method public hidebysig newslot virtual 
        instance void  EndInvoke(class [mscorlib]System.IAsyncResult result) runtime managed
    {
    }
    .method public hidebysig newslot virtual 
        instance void  Invoke(string message) runtime managed
    {
    }
}

Oh my god, a delegate is actually a class inherited from System.MulticastDelegate. It should also be noted, each delegate declaration will eventually create methods named Invoke, BeginEnvoke and EndEnvoke.

So our code will look like :
TypeBuilder tdelegate = tbuilder.DefineNestedType("", TypeAttributes.AutoClass | 
    TypeAttributes.AnsiClass | 
    TypeAttributes.Sealed | 
    TypeAttributes.Public, typeof(System.MulticastDelegate));

MethodBuilder methodBeginInvoke = tdelegate.DefineMethod("BeginInvoke",
    MethodAttributes.Public |
    MethodAttributes.HideBySig |
    MethodAttributes.NewSlot |
    MethodAttributes.Virtual,
    typeof(IAsyncResult), new Type[] { typeof(string), typeof(AsyncCallback), typeof(object) });
methodBeginInvoke.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);

MethodBuilder methodEndInvoke = tdelegate.DefineMethod("EndInvoke", MethodAttributes.Public | 
    MethodAttributes.HideBySig | 
    MethodAttributes.NewSlot | 
    MethodAttributes.Virtual,null, new Type[] { typeof(IAsyncResult)});
methodEndInvoke.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);

MethodBuilder methodInvoke = tdelegate.DefineMethod("Invoke", MethodAttributes.Public |
    MethodAttributes.HideBySig |
    MethodAttributes.NewSlot | MethodAttributes.Virtual, CallingConventions.Standard, null, new Type[] { typeof(string) });

Cool. So our type is ready to use.

Download Sample Application - 66 KB


To Deal with Bad IL

 While working with Reflection.Emit, there are times often when you find the compiler complaining about Bad IL. The common type of exception that takes place is "Common Language Runtime detected an invalid program". So is there no way out from this. Yes there is.


Microsoft provides a tool to help you in this scenarios. The tool is named as PeVerify.exe.
Once you install the Visual Studio, you can open console and try running
PeVerify your.dll 


The tool will show you where the actual error is occurring.  You can read more about PEVerify from :
MSDN Reference for PEVerify




Conclusion

Its been a fun while working with Reflection.Emit. You should note, the dynamic method could also be created more easily using Expression Trees. But still it is nice to know what basically happen inside your IL when you define a type.

Another new class that is introduced to deal with dynamic object is by inheriting from DynamicObject. These are much simpler to use from programmers point of view. But still, I hope you have liked my post.

Thank you, give your feedback and also point to me if there is anything wrong.

2 comments:

  1. There are lots of examples. I like it.

    Thanks

    ReplyDelete
  2. Fantastic article, really helped.

    Thanks

    ReplyDelete

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.