Click here to Skip to main content
15,905,683 members
Articles / Programming Languages / C#
Article

Delegates and Events - Internals

Rate me:
Please Sign up or sign in to vote.
2.91/5 (10 votes)
6 Oct 20055 min read 43.4K   26   5
This article explores the internals of delegates and events by going through the code generated by the C# compiler. Other compilers might produce slightly different IL but should be quite similar. It's assumed that the reader knows what delegates and events are and how to use them.

Introduction

When you declare a delegate, you are actually defining a class. When you declare an event, you are actually declaring a private delegate instance and a property which exposes this instance through an add and remove accessor. Well, you aren’t doing all this – it’s the compiler that does all this for you. Let’s see how.

Delegates

The custom delegate class

Let us define a simple delegate and compile it:

C#
delegate int MyDelegate(string someValue);

For the above statement, the compiler would generate the following custom delegate class:

C#
internal delegate int MyDelegate(string someValue)
{
    public MyDelegate(object object, IntPtr method);
    public virtual IAsyncResult BeginInvoke(string someValue, 
                               AsyncCallback callback, object object);
    public virtual int EndInvoke(IAsyncResult result);
    public override int Invoke(string someValue);
}

Internally, this custom delegate class inherits from System.MulticastDelegate, which in turn inherits from System.Delegate, both of which are abstract. System.Delegate implements the functionality of invoking a single method target. System.MulticastDelegate acts like a wrapper over System.Delegate to provide the functionality of invoking a chain of method targets.

The point is, delegates are internal classes and these classes inherit from System.MulticastDelegate which makes them capable of invoking multiple method targets.

Also note that if a delegate is declared inside a class, the custom delegate class would be defined within the enclosing class. In other words, the custom delegate class would be a class within another class.

Delegate constructors

As displayed in the code, the custom delegate class constructor accepts two parameters – an object and an IntPtr. The first argument represents the target object instance upon which the required method should be invoked. The second argument holds a pointer to the method that is to be invoked.

Constructors of System.MulticastDelegate and System.Delegate are similar to this custom delegate class constructor except that the second parameter for the base classes is a string. Though the code for the custom delegate class constructor is hidden, it should be safe to assume that it would figure out the method name from the IntPtr and pass it on to the base class constructors.

Within the System.Delegate constructor, a private class field of type RuntimeMethodInfo gets initialized on the basis of the value passed from the custom delegate constructor. RuntimeMethodInfo is an internal type and it inherits from System.Reflection.MethodInfo – in other words, delegates use reflection. System.Delegate would use this private field to find out which method is to be invoked.

System.Delegate also defines a public static CreateDelegate method which creates and returns a delegate type. Both this method and the constructor use the private InternalCreate method which handles the actual delegate type creation process.

As mentioned before, System.MulticastDelegate provides the functionality to invoke a chain of method targets. To accomplish this, System.MulticastDelegate defines a private System.MulticastDelegate field, which would represent any previous delegate instance (a pattern similar to a linked list). When a delegate or an event is invoked, each instance in this list would be invoked in the sequence they were registered.

Also note that the virtual System.Delegate.GetInvocationList method is overridden in System.MulticastDelegate to return a System.Delegate array in the sequence the targets were registered.

Invoke methods

In the custom delegate class, BeginInvoke and Invoke methods are generated with respect to the delegate signature defined. This is how the delegates ensure type safety.

Also note that though the Invoke method is marked as override, there is no Invoke method declared in either of the base classes. It could be safe to assume that the compiler generates such a method at build time which the custom class overrides.

Delegate registration

Let us now expand on the existing code and add a delegate class field and register a method target as follows:

C#
class Class1
{
    [STAThread]
    static void Main(string[] args)
    {
        MyClass obj = new MyClass();
        obj.myDelegateInstance = new MyDelegate(Class1.Target);
    }

    static int Target(string someValue)
    {
        return 0;
    }
}

delegate int MyDelegate(string someValue);
class MyClass
{
    public MyDelegate myDelegateInstance;
}

The statement marked in bold uses the = operator to attach a new delegate target. The compiler would generate no custom code for the = operator. However, if we were to use the += operator like the statement below, the results would be different:

C#
class1.myDelegateInstance += new MyDelegate(Class1.Target);

For the above statement, the compiler would produce the following code:

C#
class1.myDelegateInstance = 
  ((MyDelegate) Delegate.Combine(class1.myDelegateInstance, 
                                   new MyDelegate(Class1.Target)));

The reason for this change is that since we are using the += operator, the compiler needs to attach a new delegate target to an existing chain of delegate targets. The statement generated above assists in registering multiple target methods, by calling the Delegate.Combine method. Delegate.Combine invokes Multicast.Combine which initializes the private System.Delegate class field we talked about before (the one which holds the previous delegate instance). A chain of delegate targets is thus created.

Events

Events are just properties (or at least property-like constructs) which the complier generates when you declare an event. Each event is a wrapping property over an existing private delegate instance field. This property exposes only add and remove accessors, which prevents operations like = on an event.

Let us modify our class to declare an event:

C#
delegate int MyDelegate(string someValue);
class MyClass
{
    public event MyDelegate myEventInstance;
}

The compiler generated code for the above code will be as follows:

C#
internal class MyClass
{
    public event MyDelegate myEventInstance { add; remove; };
    private MyDelegate myEventInstance;

    public MyClass();
}

Note the addition of the public myEventInstance property-like construct over the private delegate instance myEventInstance. When you register and deregister handlers on events, it is the public custom event property you would access. Since the actual delegate instance myEventInstance is private, the only operations exposed are those that are accessible through the public myEventInstance custom event property – the add and remove operations. This is the reason why events support only += and -= operators.

The following is how the add and remove handlers of the custom event property will look like:

C#
public void add_myEventInstance(MyDelegate value)
{
    this.myEventInstance = 
        ((MyDelegate) Delegate.Combine(this.myEventInstance, value));
} 

public void remove_myEventInstance(MyDelegate value)
{
    this.myEventInstance = 
        ((MyDelegate) Delegate.Remove(this.myEventInstance, value));
}

The code is exactly the same as with adding and removing targets with delegates, which we saw earlier. You could also see that the add and remove handlers are just like the get and set accessors generated for properties.

The point is, events are just properties over private delegate instances which allow only add and remove operations.

Conclusion

This brings us to the end of this article. Hope it was an enjoyable ride. If someone could shed light on the hidden code generated by the compiler and the assumptions I have taken, please let me know – I would be most grateful and would update the content; thanks!

Though this topic may not be of much use in most scenarios, internals are usually interesting. You might also want to try creating your own delegate classes using Reflection, just for fun!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
India India
Rakesh Rajan is a Software Engineer from India working at Technopark, Trivandrum in Kerala. He is a Microsoft MVP and an MCSD (.NET) with a few other certifications, and had been working in .NET for the past 3 years. He graduated majoring in Computer Science during his memorable days at Ooty (a wonderful hill station in Southern India). You can find him posting at newgroups, writing articles, working on his own projects or taking some time off by listening to music by Enya or Yanni, or reading an Archer or Sheldon.

Find his online publications here.

Rakesh blogs at http://rakeshrajan.com/blog/ and maintains a site http://rakeshrajan.com/.
He used to blog at http://www.msmvps.com/rakeshrajan/.

Drop him a mail at rakeshrajan {at} mvps {dot} org.

Comments and Discussions

 
QuestionDelegate call sequence ? Pin
Chethan Thopaiah9-Oct-07 4:46
Chethan Thopaiah9-Oct-07 4:46 
GeneralEvents vs. Delegates Pin
Julien Couvreur20-Feb-06 13:18
Julien Couvreur20-Feb-06 13:18 
NewsJust updated the article Pin
Rakesh Rajan6-Oct-05 14:42
Rakesh Rajan6-Oct-05 14:42 
I didn't like the way the article came to when I read it after submission.

I did another cycle of modifications and now hopefully it reads better. Smile | :)


Rakesh

GeneralRe: Just updated the article Pin
Rakesh Rajan11-Oct-05 5:59
Rakesh Rajan11-Oct-05 5:59 
GeneralRe: Just updated the article Pin
Ice_Icarus4-Apr-06 8:48
Ice_Icarus4-Apr-06 8:48 

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.