Jul 26

As we’ve seen in the previous post, delegates are really easy to use: you create a new delegate definition and then instantiate it through the new operator, passing it a compatible method reference. However, there’s a lot more going on behind the scenes and that’s what we’ll start seeing in this post. The first thing we need to understand is what happens when the compiler sees a delegate definition like the one show in the next snippet:

public delegate void Logger( String info );

In these cases, the compiler will automatically transform the previous delegate expression into a new class which inherits from MulticastDelegate and looks like this:

public class Logger:MulticastDelegate {
public Logger(Object aux, IntPtr method ) { }
public virtual void Invoke( String info ) { }
public virtual IAsyncResult BeginInvoke(
String info, AsyncCallback cb, Object aux ) { }
public virtual void EncInvoke( IAsyncResult result ){}

Notice that you can’t really reuse the MulticastDelegate type in C# because the compiler won’t allow you to use those classes directly from your C# code.

As you can see, a delegate definition expression ends up generating a new class with a new constructor and three methods: Invoke, BeginInvoke and EndInvoke. Lets start with the constructor. The two parameters it receives are used for initializing, respectively, the MulticastDelegate’s target and method private fields. When we use a delegate to call a static method, the target field is initialized to null. On the other hand, when we use it to call an instance method, the target field is initialized with a reference to the object whose instance member is being invoked. In both cases, the private method field references the method that should be called. Btw, you can access these values through the inherited Target and Method properties:

//we’re passing a static method
var aux = newLogger( ConsoleLogger );
Console.WriteLine( aux.Target == null );//true
Console.WriteLine( aux.Method );

If you take a look at the MulticastDelegate’s code, you’ll quickly notice that it doesn’t define these properties. In fact, these properties are exposed by the Delegate type, which is used as base by the MulticastDelegate type. To be honest, I really don’t have any good explanation for having two delegate classes since our delegates definitions end up always creating new classes which expand the MulticastDelegate type (and no, you can’t either create a new type which uses Delegate as a base type in C#)…Going back to the snippet, I believe I should mention  that the Method property returns a reference to a MethodInfo instance (this type provides us access to a method metadata – this means you also use it, for instance, to get the name of the method that is being invoked).

Now that we understand what happens when we instantiate a new delegate, it’s type to see how invocation works. So, what happens when the compiler finds something like this:

var aux = newLogger( ConsoleLogger );//seen this
aux( “hello” );//what happens here?

If we take a peek at the generated IL, we’ll find the following code:


As you can see, our aux method called is translated into an instance Invoke method call. Btw, nothing prevents us from doing that call from our C# text:

aux.Invoke( “hello” );

And yes, it does compile without any errors (thank you for asking!). Internally, Invoke will use the information returned by the Target and Method properties to invoke the method. As you’ve probably inferred, BeginInvoke and EndInvoke can be used to invoke the methods asynchronously (and have been used for several years as a way to run code in parallel). We’ll probably return to the async methods in a future post…

But there’s more…the astute reader has probably noticed the “Multicast” string in the MulticastDelegate name’s type. It’s obvious that using indirection to invoke a method is really powerful, but what about using a delegate to chain the execution of several methods? We’ll see how we can do this in the next post. Stay tuned for more.

1 comment so far

  1. Harley Pebley
    5:58 pm - 7-27-2011

    Thanks for the info!

    A couple comments:
    1) Did you know the first couple code snippets are displaying mostly without spaces? (I can parse publicvirtualvoid, but the computer probably can’t. 🙂

    2) Apparently the split between Delegate and MulticastDelegate classes is historical. See Jon Skeet’s answer here:

    3) Any thoughts on using the system defined delegate types: e.g. Action, Action, Func, etc.? Personally, I tend to go back and forth on whether they’re good or not. On one hand, they make coding easy. On the other being generic, they don’t convey meaning well.