Pages

Tuesday, March 15, 2011

Internals of Events

If you are looking already at my internal series, you must by now know what does an entry from Internal series means. Yes, In each of those articles, I have tried to at least give some basic idea about the usage and later tried to show you some of the internal process that is happening to achieve the technique. In this post, as the name suggest, I will show how the event system works in .NET (or rather C#) and how this system is actually achieved into the system.

The Basics

Event is a special object in .NET which allows you to give notification to the external world. As far as I am concerned, its a means of communication between the object to the external world, or rather a means of inversion of control to the caller. Now what exactly an event does ?




Literally it maps to an existing delegate that acts as a Type of it which eventually points to a method. Thus when the event is subscribed, it needs a method which the object will call at certain time when event is raised in the system. Thus basically an event is a structured way of calling a delegate into a class. Now let me examine this using a code :


public class Notifier
{
    private event EventHandler _mycustomEvent;
    public event EventHandler MyCustomEvent
    {
        add
        {
            this.Counter++;
            this._mycustomEvent += value;
            Console.WriteLine("Event Handler Added");
        }
        remove
        {
            this.Counter--;
            this._mycustomEvent -= value;
            Console.WriteLine("Event Handler Removed");
        }
    }

    public virtual void OnMyCustomEvent(EventArgs ea)
    {
        if (this._mycustomEvent != null)
            this._mycustomEvent(this, ea);
    }
    public int Counter { get; set; }

    public void CallSubroutine()
    {

        Console.WriteLine("***********************Subroutine start**********************");

        this.OnMyCustomEvent(EventArgs.Empty);

        Console.WriteLine("After event is Raised");

        Console.WriteLine("Lets raise again");

        this.OnMyCustomEvent(EventArgs.Empty);

        Console.WriteLine("**********************Ending...*******************************");
    }
}

So, here is a class called Notifier, which notifies an event or raises an event during the execution of the method CallSubroutine. Hence,  if you have already subscribed for an event, the event gets raised twice for each calls to this method.

Therefore, when I run the code with an EventHandler subscribed to the event MyCustomEvent like this below :

static void Main(string[] args)
{
    Notifier nfier = new Notifier();

    nfier.MyCustomEvent += new EventHandler(nfier_MyCustomEvent);

    Console.WriteLine("Added 1st one eventhandler");

    nfier.CallSubroutine();

    nfier.MyCustomEvent+=new EventHandler(nfier_MyCustomEvent);

    Console.WriteLine("Added another eventhandler");

    nfier.CallSubroutine();

    Console.Read();
}

static void nfier_MyCustomEvent(object sender, EventArgs e)
{
    Notifier nfier = sender as Notifier;

    Console.WriteLine("Event is raised with Handler Counter : {0}", nfier.Counter);
}

It generates output like this :


If you see the code, you should see, the first call produces the Counter 1 as initially before calling CallSubroutine for the first time, it has subscribed the nfier_MyCustomEvent for once. But for the second call, the nfier_MyCustomEvent is again subscribed, and hence the method is called twice for each event raised.

Thus you should clear that, based on Multicast Delegates, you can assign more than one method to an EventHandler. Once you subscribe to the event, you will get notified for that subscription. You might subscribe as many methods as you want, but each of them should follow the signature of the Handler. So that when, the event is raised, the object will call all the methods subscribed to that event.

The Internals

Well, before I turn on with the internals, I must let you know that events in .NET is ThreadSafe and uses Multicast Delegates internally. It is provided with .NET to give a standard way of notifying the external world rather than dealing with delegates directly. If you do not know about internals of delegates, please read my post on delegates first. Now let me try my favorite tool "Reflector" to see what is going on with the class I just wrote before.

Well, basically the class contains mainly 3 things :

1.  private event of type EventHandler named "_mycustomevent"
2. public event accessor named "MyCustomEvent" which gives an interface for add and remove operation for the event.
3. public virtual OnMyCustomEvent method which I call from code when I want to raise the event.

Thus eventually the actual event variable is _mycustomevent which is not exposed outside. We use event accessor instead to ensure we have an block interface just like properties when EventHander is added and removed from the actual event. Now if I look into reflector :


Now if you compare the code inside the reflector with the code that you wrote, you would easily get the idea that there is no type called event in .NET CLR. Events are a way to subscribe a delegate which could later be called. So eventually the variable _myevent eventually creates an event accessor, which adds up delegates to the delegate invocation list (a common function of Multicast Delegate) using Delegate.Combine. Such that whenever the event is subscribed, it creates an object of multicast delegate eventhandler object in IL and adds up the method passed to and uses Delegate.Combine to form the invocation list. As you already know delegates internally calls all its methods configured in its invocation list, the calls will be automatically managed. Now lets show up how invokation list is built inside delegates :


So basically each MulticastDelegate holds an array of MethodInfo internally which is upcast as objects.  So when we pass a method into it, it adds up an entry into its invocation list and maintains it for calls. Delegate.Combine calls for this method to add the event handler to the existing invocation list.

Another major thing that you should notice in the Notifier code is that, the accessor compares the method during addition and removal using Interlocked.CompareExchange. This method is ThreadSafe. If you look back in code written before .NET 4.0 you would see the whole body inside the event accessor is written inside lock statement, which is rectified in .NET 4.0 to ensure only locks are imposed when required.

Finally, if you look into the IL, you would find that eventaccessor is a special block which has two interface attached with it :

The .addon is used for addition of an event subscription while the removeon is used for its removal. The methods which those are attached to are add__mycustomEvent and remove__mycustomEvent. The double underscore(__) is used to ensure that method name does not collide.

Conclusion

Basically if you understood properly, there is no difference as such than manually use Delegate.Combine and Delegate.Remove than that of events. But events produce more structured interface for your code and hence should be used in libraries. Even while using events you should also follow .NET event patterns (even though it is not mandatory) which defines two parameters first being sender and second being an object derived from EventArgs. You might also use EventHandler generic method to define your own custom EventArgs. 

Download Sample Code - 37 KB

I hope you have found fun reading this post.

Thank you.

No comments:

Post a 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.