Wednesday, January 9, 2013

Delegates and Events in C#

Delegates and Events in C#

Delegates

Delegate is an object which can be used to call any method with the same method declaration. Specifically it defines the method's parameter types and return types.

Below is an example which defines a delegate type
delegate int Converter(int val);

Converter  is compatible with any method which takes an int value as parameter and returns an int value.
static int ConvertCMtoMM(int val) { return val * 10; }

Onces you assign the delegate variable with a method name as value, a delegate instance is created
Converter con = ConvertCMtoMM;

You can invoke this method like :
int convertedValue = con(100);

So this is a way of decoupling the caller from the actual method. The caller calls the delegate and the delegate calls the method.

Multicast Delegates

 A delegate instance can be targeted to multiple methods which is called multicast delegate. Considering the above delegate, we can add more references to the delegate by using += and we can remove references using -=.

        static void Main(string[] args)
        {
            Converter con = ConvertCMtoMM;
            con+= ConvertMetertoCM;
            con+=ConvertKMtoMeter;
            con -= ConvertCMtoMM;
            int value = con(1000);
        
        }

        static int ConvertCMtoMM(int val) { return val * 10; }
        static int ConvertMetertoCM(int val) { return val * 100; }
        static int ConvertKMtoMeter(int val) { return val * 1000; }

Invoking con will now call all the 3 methods one after other in the order in which they are added to the delegate. If the return type for the multicast delegate is not a void, then the delegate will return only the value from the last method being called by the delegate. In most cases, multicast delegates will be used with methods having void return types.

Events

In the delegate scenario, there are basically 2 types of participants.
1. Broadcaster
2. Subscriber

Broadcaster will be the owner of the delegate field and he decides when to do the broadcast.
Subscribers are the audience for the broadcast. They can decide when they want to watch the broadcast or when they don't want to watch using the += and -= on the broadcaster's delegate field.

Events can be considered as a pattern which are made for the broadcasting subscription policy. So the question is we are able to do it with the delegates, then why do we need an event.

The main purpose of the event is to prevent the subscribers from interfering in the broadcast. Their job is to only listen or if they don't want, then unsubscribe and go.

If a delegate is used in place of an event for a broadcasting, the following things can happen:
1. Clear all subscribers by setting the delegate instance to null.
con = null // this will clear all the subscriptions.
2. Do the broadcast by invoking the delegate.
con(1000);
3. Replace other subscribers by reassigning by using = instead of +=.
con = ConvertCMtoMM;

Now lets see how to create an event. It is very simple if you know how to create a delegate. Just add event keyword before the delegate name.

    //delegate
    public delegate void TemperatureChangedHandler(double oldTemperature, double newTemperature); 
    public class TemperatureBroadcast
    {
        // this class might get the temperature reading from a device output which sets the temperature value;
        public TemperatureBroadcast()
        {
            temperature = 28.00;
        }

        private double temperature;
        public event TemperatureChangedHandler TemperatureChanged;  //event
        public double Temperature {
            get
            {
                return temperature;
            }
            set
            {
                if (value == temperature)
                    return;
                double oldTemperature = temperature;
                temperature = value;
                if (TemperatureChanged != null) //check if there are subscribers listening for the temperature  
                                                                 //variations.
                {
                    TemperatureChanged(oldTemperature, temperature);
                }
            }
        }
    }


static void Main(string[] args)
        {
            TemperatureBroadcast broadcast = new TemperatureBroadcast();
            broadcast.TemperatureChanged  +=new TemperatureChangedHandler(Broadcast_TemperatureChanged); //subscribing to temperature broadcast.
        
        }

//Anytime the temperature is changed, this subscriber is notified as long as he is subscribed.
        static void Broadcast_TemperatureChanged(double oldTemperature, double newTemparature)
        {
            Console.WriteLine("The temparature has changed from {0} to {1}", oldTemperature, newTemparature);
        }

Now to try out the issues which can happen with a delegate in case of broadcast scenario instead of an event, remove the event keyword from the delegate instance and try calling the delegate from the main method 

TemperatureChanged(100, 200);

All other subscribers will get this temperature information broadcast by a subscriber.
Even you will be able to assign TemperatureChanged = null which will remove all the subscribers.
Also you can replace other subscribers by reassigning the method to the delegate TemperatureChanged = broadcast_TemperatureChanged;

Thanks everybody for reading my blog...
Ratheesh Rahulan

1 comment:

  1. Good post...
    One con on this is the event publisher and subscriber will be tightly coupled. As well as there are chances of memory leak if the subscriptions are not unsubscribed correctly.

    ReplyDelete