Sunday, July 22, 2012

Delegates and Events

Today I’d like to write about the events, but because I cannot talk about events without delegates, I’ll cover those as well in this post.
To make the article easier to understand (for new developers at least) let me describe a more real-world example so we’ll be able to model things from it. One such example can be the fire brigades' work – how they react to fires, how they being notified about fires and so on.
So thinking about it I assume that every fire brigade works for some kind of center, which control’s its work. And every such center has several brigades under its control. So let’s model that center as FireAlarmingCenter class described below:
public class FireAlarmingCenter
{
}

As we said earlier the FireAlarmingCenter will be one who notifies about fire alerts to the fire brigades. Before we will be able to add that notification capability to the FireAlarmingCenter class, we need to define what the notification will look like. and here where the delegates come in. To specify the details about the specifics of the notification we need to describe those through a delegate, which will define a signature of a method it can reference to, or a method, which can be used as a handler for an event of that delegate type.

public delegate void FireAlarmEventHandler(FireAlarmingCenter argCenter, AlarmDetails argDetails);

Now let’s add the event definition to the FireAlarmingCenter type as follows:

public event FireAlarmEventHandler FireAlarm;

Great, now we have the FireAlarmingCenter type, so let's see how this event is going to be consumed.
As we have said already, the consumers of FireAlarm event will be the fire brigades, which we can model like this:

public class FireBrigade
{
    public FireBrigade(FireAlarmingCenter argCenter)
    {
        if (argCenter == null)
            throw new ArgumentNullException("argCenter");

        this.Center = argCenter;
    }

    public FireAlarmingCenter Center
    {
        get;
        private set;
    }
} 

Now we’ve created the base plumbing so the FireBrigade will have the reference to the FireAlarmingCenter it’s “working for”. But there is still a hole there – the FIreBrigade doesn’t somehow “listen” to the FireAlarm event of its center. To do so, we just need to aappend the following line to the FireBrigade class constructor:

this.Center.FireAlarm += new FireAlarmEventHandler(this.HandleFireAlarm);

Although the C# 4.0 language let’s the developers to skip the delegate type after += operation, when registering for events and specify only the method name, which is going to work as handler for that event, I like that style, so its really visible from code what type exactly the event is, so to the handler method signature is. So that line will assume that we’ve defined the “HandleFireAlarm” method already in the FireBrigade class, which will contain logic about what to do in case of a fire.
Now let's see how the FireAlarmingCenter will notify the FireBrigade-s about a fire. I’m not going to write or guess what the conditions are on when to notify it, but am really going to concentrate on how it will be fired. The most simple way to do that is as follows:
  
private void OnFireAlarm(AlarmDetails argDetails)
{
    if (this.FireAlarm != null)
    {
        this.FireAlarm(this, argDetails);
    }
}

Very nice and simple, it’s really not what we want. So let’s go and see what is happening behind those lines ?
So the first line in the method checks whether there are any listeners registered for the FireAlarm event or not. Next, if there are listeners to the FireAlarm event, the most important thing is going to happen – the event is going to be called. To answer the question – “what means an event is called?”, I’ll need to go back a bit to describe what the delegate is.
So the delegate is a reference of a method, but not only – it is actually a list of references to methods, which all are matching the signature of the delegate. Actually, the definition of the delegate we’ve created is being translated by the compiler to a class definition with the name of that delegate and deriving from an abstract class called “MulticastDelegate”:
 
public class FireAlarmEventHandler : MulticastDelegate
{
}

So now we can approach to an event as a field of a specified delegate type in a class it’s defined in. So when in the FireBrigade class’s constructor the += operation on the FireAlarm event is being called, it actually adds a new method reference (which is specified on the right from the += operator) to the delegate invocation list.
Now, when the event (in this case the FireAlarm) is being called, the following happens:An iterator is being created which iterates over all the registered methods to that delegate instance, and each element of that iterator (in this case a method reference) the method is being called with parameters passed in to the event call. But this seems to contain a problem.Imagine a situation when during the event listeneres execution (iterating over the list of handler methods) a new FireBrigade will be constructed for that FireAlarmingCenter – the invocation list will be modified so an exception will be thrown here.But don’t worry – there is a very handy way of handling this and I really like it. If we’ll modify the code like this:

private void OnFireAlarm(AlarmDetails argDetails)
{
    FireAlarmEventHandler tmpEvent = this.FireAlarm;
    if (tmpEvent != null)
    {
        tmpEvent(this, argDetails);
    }
}

on the first line in the method the invokation list of the delegate will be copied and the next operations will be working on the copied list including the invocation itself) so adding or removing any new handlers to the FireAlarm event won’t affect the invocation any more. This technique is usually referenced as “Event Invoke Pattern”.
Visual Studio has a very nice snippet for this: “invoke”. Just type invoke in VS.Net and hit Tab twice (Tab+Tab) and you’ll get the code automatically appear in your source.

So I think that’s all for this post. Good luck with your development.
Post a Comment