Exploring C# Delegates


Delegates are a kind of type safe functionpointers which are actually declared as class derived fromSystem.MulticastDelegate. There are few rules how to write that class.First you must declare .ctor and Invoke methods, if you like to haveasynchronous callback involved there are two more methods just for thatcase BeginInvoke and EndInvoke.
Except for these two or four methods nothing else should be declaredand these declarations must be without implementation (empty body).This is what it looks like:

    .method public hidebysig specialname rtspecialname 
    instance void .ctor(object 'object',
    native int 'method') runtime managed
    {
    } // end of method WildCard::.ctor

    .method public hidebysig virtual instance int32 
    Invoke() runtime managed
    {
    } // end of method WildCard::Invoke 

If you like you may delete hidebysig and everything will still working.Do not delete anything else. First argument supplied to .ctor isinstance of the class that defines the target method and second ispointer to the method to be called. Following is more in conformancewith materials available from Microsoft:

    .method public specialname rtspecialname 
    instance void .ctor(object Instance, void * Method) runtime managed

    {
    } // end of method WildCard::.ctor

    .method public virtual instance int32 
    Invoke() runtime managed
    {
    } // end of method WildCard::Invoke

Type of Invoke must match type of method to be called, also list ofparameters and their respective types must match. BeginInvoke must beof return type System.IAsyncResult. It accepts three parameters:thefirst is the same as that being used to call Invoke, type of second isSystem.AsyncCallback and third is the same as first parameter from.ctor. EndInvoke returns and accepts the same as Invoke plus one extraparameter of type System.IAsyncResult. Now we have all the definitions.Since we are planning to use debugger create shortcut in your �SendTo�folder and point it to "C:\ProgramFiles\Microsoft.NET\FrameworkSDK\GuiDebug\DbgCLR.exe".
As always we will first write some code in high level language anddisassemble P.E. to see what is going on. Save following as example.cs:

using System;
delegate int WildCard();
class Worker
{
    int m_w;
    public Worker(int w)
    {
        m_w=w;
    }
    public int Multiply()
    {
        return 2*m_w;
    }
}
class User
{
    public static void Main()
    {
        int c=2;
        Worker w=new Worker(c);
        WildCard v=new WildCard(w.Multiply);
        Console.WriteLine("Argument is {1}\nResult is {0}",v(),c);
    }
}

Compile it from command line using �csc example.cs /debug�. Executingexample.exe from command line should produce:

    Argument is 2
    Result is 4

Right click example.exe and from �Send To� pick �DbgCLR.exe�. MicrosoftCLR Debugger will appear without any code loaded. Press F11 to stepthrough code and example.cs will show up. 

explor1 Exploring C# Delegates
Microsoft CLR Debugger in action.

If you don�t have Command Window activate it from View->OtherWindows menu. To see Disassembly you can right click on code window andselect �Go To Disassembly� or customize debug toolbar. Command Windowworks very much as Immediate window from VB 6. Typing c=2 and pressingenter will set value of c or typing ?c and pressing enter will retrievevalue of c into Command Window. Pressing F11 step through code and ifyou like observe changes in Locals window.
Next we will send example.exe to ILDASM using the same trick describedin my article �Modest introduction to IL assembly language�. If we dumptreeview this is what we get:

    ___[MOD] C:\Documents and Settings\current\My Documents\asm\finale\example.exe
    | M A N I F E S T
    |___[CLS] User
    | | .class private auto ansi beforefieldinit 
    | |___[MET] .ctor : void()
    | |___[STM] Main : void()
    |
    |___[CLS] WildCard
    | | .class private auto ansi sealed 
    | | extends [mscorlib]System.MulticastDelegate 
    | |___[MET] .ctor : void(object,native int)
    | |___[MET] BeginInvoke : class [mscorlib]System.IAsyncResult(class [mscorlib]System.AsyncCallback,object)
    | |___[MET] EndInvoke : int32(class [mscorlib]System.IAsyncResult)
    | |___[MET] Invoke : int32()
    |
    |___[CLS] Worker
    | | .class private auto ansi beforefieldinit 
    | |___[FLD] m_w : private int32
    | |___[MET] .ctor : void(int32)
    | |___[MET] Multiply : int32()
    |

Please note that our target Worker.Multiply is not receiving anyparameter so parameter list for Invoke, BeginInvoke and EndInvoke isshorter than usual. Now dump code as dis_example and compile it fromcommand line using �ilasm dis_example /deb�. Send dis_example.exe toDbgCLR.exe and press F11. This time dialog box will appear asking youto locate dis_example.il. After pointing to location of dis_example.ildebugger will load our file and pressing F11 we can examine flow ofprogram checking from time to time Locals and values in Command Window.

explor2 Exploring C# Delegates
Locals window towards end of program.

And finally we can start altering assembly code. If you like removeBeginInvoke and EndInvoke from WildCard declaration. Compile it and runit. It still works fine. Next I think that it would be nice to haveWildCard derived straight from System.Delegate and maybe to changesecond parameter of .ctor from native int to void* so that it is morelike examples available from Microsoft.
Change WildCard declaration to look like this:

    .class private auto ansi sealed WildCard
    extends [mscorlib]System.Delegate
    {
        .method public specialname rtspecialname 
        instance void .ctor(object Instance, void * Method) runtime managed

        {
        } // end of method WildCard::.ctor

        .method public virtual instance int32 
        Invoke() runtime managed
        {
        } // end of method WildCard::Invoke

   &nbs
p;    .method public virtual 
        instance class [mscorlib]System.IAsyncResult 
        BeginInvoke(class [mscorlib]System.AsyncCallback callback,
        object Instance) runtime managed
        {
        } // end of method WildCard::BeginInvoke

        .method public virtual 
        instance int32 EndInvoke(class [mscorlib]System.IAsyncResult result) runtime managed
        {
        } // end of method WildCard::EndInvoke

    } // end of class WildCard

Also change code in Main where you are calling WildCard::.ctor so that your code looks like this:

    .method public hidebysig static void Main() cil managed
    {
        .entrypoint
        // Code size 50 (0×32)
        .maxstack 3
        .locals ([0] int32 c,
        [1] class Worker w,
        [2] class WildCard v)
        IL_0000: ldc.i4.2
        IL_0001: stloc.0
        IL_0002: ldloc.0
        IL_0003: newobj instance void Worker::.ctor(int32)
        IL_0008: stloc.1
        IL_0009: ldloc.1
        IL_000a: ldftn instance int32 Worker::Multiply()
        IL_0010: newobj instance void WildCard::.ctor(object,
                                                                           void *)
        IL_0015: stloc.2
        IL_0016: ldstr "Argument is {1}\nResult is {0}"
        IL_001b: ldloc.2
        IL_001c: callvirt instance int32 WildCard::Invoke()
        IL_0021: box [mscorlib]System.Int32
        IL_0026: ldloc.0
        IL_0027: box [mscorlib]System.Int32
        IL_002c: call void [mscorlib]System.Console::WriteLine(string,
                                                                           object,
                                                                           object)
        IL_0031: ret
    } // end of method User::Main

If you don�t change it that will compile but will generate run time error.Compile it and check using debugger type of WildCard.
If you were reading �C:\Program Files\Microsoft.NET\FrameworkSDK\ToolDevelopers Guide\docs\Partition II Metadata.doc� there is under 13.6Delegates following:

    "A better design would be to simply have delegate classes derive directly from System.Delegate." 

I hope that this article shows how that could be done using ILASM.
If you have any question or suggestion I can be reached at filipbulovic@hotmail.com if time allows I will come back to you.

Related Articles :

Twitter Digg Delicious Stumbleupon Technorati Facebook Email

No comments yet... Be the first to leave a reply!