Tuesday, February 28, 2012

Delegates & Events (C#)


Delegates and events are one of the coolest features provided in C#. Both delegates and events are associated with each other, that’s why they are always talked together. For example, if we are using events we should have delegates to support them. In fact delegates & events in conjunction provides a way to implement one of the nicest pattern-the observer pattern (the publisher-subscriber pattern or pub-sub pattern).

            Delegate as the word “literally” means- “a thing which has been assigned to represent something”. In C# we declare a delegate to represent a method. Delegates in C# are always compared with function pointers in C++, C, which in fact is true. Technically delegate is something which holds the address of a method, or address of multiple methods.  Let’s look at an example and understand as to how we declare a delegate:-

private delegate void MyDelegate();
private delegate string MyDelegate(string str);

 The above declaration means that the delegate (MyDelegate) can hold the address of any function which returns nothing (void) and accepts no parameter. Similarly the second declaration means that the delegate can hold the address of any method which accepts one string parameter and returns string. Let’s use the second delegate in a program to understand how we can use delegate to call method(s).

class Program
    {
        private delegate string MyDelegate(string str);
        static void Main(string[] args)
        {
            MyDelegate myDelg = new MyDelegate(MyDelegateMethod1);
            Console.WriteLine(myDelg("Test 1"));
           
            myDelg += MyDelegateMethod2;
            Console.WriteLine(myDelg("Test 2"));

            // Would give a compile time error as the signature of  MyDelegateMethod3 does not match with delegate definition

            //myDelg += MyDelegateMethod3;
            Console.ReadLine();
        }

        private static string MyDelegateMethod1(string strParam)
        {
            return "This is from first method-"+strParam;
        }

        private static string MyDelegateMethod2(string strParam)
        {
            return "This is from second method-"+strParam;
        }

        private static string MyDelegateMethod3(string strParam,string strParam2)
        {
            return "This is from third method-" + strParam;
        }
    }

From the above program it is clear how we can use call multiple methods from a single delegate, this is called a multicast delegate. Notice that when we try to assign the address of MyDelegateMethod3, it gives a compilation error highlighting the fact that delegates are type safe. Also notice how we are invoking the methods and passing the parameters to it (by the use of brackets myDelg("Test 1")).

Behind the scene Delegates are not any kind of magic, when you declare a delegate, the compiler actually generates an anonymous class which is derived from System.MulticastDelegate which in turn is derived from the base System.Delegate. In fact it would be correct to say that when you declare a delegate you are actually declaring a class (though the compiler internally declares the class for you). That is one of the reason why you can declare a delegate anywhere you can declare a class i.e. inside a namespace, inside another class. Try declaring a delegate in a method, you would not be able to do that, think of the reason for that.

Generic Delegates Instead of declaring a delegate every time with different parameters and return types, C# provides Action<T> and Func<T> delegates. The Action<T> delegate can be used to reference a method with void return type. Similarly the Func<T> delegate can be used to reference a method with a return type. Both of these Action and Func delegates can accept up to 16 parameters. Let’s look at an example which would make both of them clear.


class Program
    {
        static void Main(string[] args)
        {
            Action delegAction = delegActionNoParams;
            delegAction();

            Action<string> delegAction1 = delegActionWithOneParam;
            delegAction1("Parameter1");

            Action<string, string, string> delegAction2 = delegActionWithMultipleParams;
            delegAction2("Parameter1", "Parameter2", "Parameter3");

            Func<string> delegFunc = delegFuncNoParams;
            Console.WriteLine(delegFunc());

            Func<string, string, int> delegFunc1 = delegFuncMultipleParams;

            // Would have return value 9 from the method.
            int returnValue = delegFunc1(string.Empty, string.Empty);
            Console.ReadLine();
        }

        private static void delegActionNoParams()
        {
            Console.WriteLine("Call to delegate with no params and no return types");
        }

        private static void delegActionWithOneParam(string param1)
        {
            Console.WriteLine("Call to delegate with param={0} and no return types",param1);
        }

        private static void delegActionWithMultipleParams(string param1,string param2,string param3)
        {
            Console.WriteLine("Call to delegate with params={0},{1},{2} and no return types",param1,param2,param3);
        }

        private static string delegFuncNoParams()
        {
            return "Func delegate with no params";
        }

        private static int delegFuncMultipleParams(string param1,string param2)
        {
            return 9;
        }
    }

The program above is self explanatory. It gives us a clear idea as to how we can make appropriate use of Action and Func delegates and avoid unnecessary “delegates” declaration. Imagine how many delegates you would have to declare for the above program if you were not using Action and Func. Also Action and Func comes in very handy when we have to dynamically invoke methods, you can realize its strength by imagining such a scenario.

            Lambda expression can be of great help with delegates. Below is the example which simplifies the code involving delegates:-

 static void Main(string[] args)
        {
            Func<string, string, string> lambdaDeleg = (str1, str2) => string.Format("This is an example of lambda delegate with two paramters={0} and {1}", str1, str2);
            Console.WriteLine(lambdaDeleg("Parameter1","Parameter2"));
            Console.ReadLine();
        }

The above code demonstrates the use of lambda expression with delegates. Notice how we can specify the parameters and the method body with the lambda(=>).

                       Events

    Having learned delegates lets try making real use of them. Delegates are used a lot to support events. Let’s dig deep into what actually are events. Literally it means “happening of something”. Since our OOPS is based on real world so events are also accommodated in our programming scenario- it means occurrence into an application. As I mentioned before that Delegates & Events together provides a very famous pattern-the observer pattern or the publisher-subscriber pattern. (We will talk about this pattern in our next article). Have you ever used events or delegates before? - A definite yes. In fact you have been using those, quiet a lot. For example you are displaying a message box on click of a button. Here the “button click” is the event and the method body where you write the code to show the message box, is the supporting delegate for the event. Let’s simply jump to a real world example where we would make use of events.

            You often might have come across various web pages where they have a “Notify Me” link. This notification can be related to new product availability or something else. Imagine we have to implement such a scenario where a departmental store would want to notify the shoppers through emails that a new stock has arrived. Events-delegates are very efficient tools for such a scenario. Below is the complete code which implements the idea discussed above. Let’s take the snippets one by one and understand them.

public class NewProductEventArgs:EventArgs
    {
        public string NewProduct { get; set; }
        public NewProductEventArgs(string productName)
        {
            this.NewProduct = productName;
        }
    }
This class serves as placeholder for the argument that events can have. An event can have no arguments as well but usually we define this class to contain objects related to our business requirements. (We will find out its use in next class)

public class DepartmentStore
    {
        public event EventHandler<NewProductEventArgs> NewProductArrived;
        public void NewProduct(string productName)
        {
            if (NewProductArrived != null)
            {
                NewProductArrived(this, new NewProductEventArgs(productName));
            }
        }
    }
This class declares an event NewProductArrived. Notice how this event is declared. The EventHandler is a generic delegate which represents the method that will handle the event. The generic parameter for this is the class which we have created above (NewProductEventArgs). The delegate “EventHandler” requires its generic parameter to be derived from EventArgs that is why we have declare the class as public class NewProductEventArgs:EventArgs. When we use the delegate “EventHandler” it requires that the function definition for this should have first parameter as object and second parameter of type derived from EventArgs.
We have then created the method NewProduct. This method would act as our publisher which would notify the subscribers (subscriber class coming below) that an event has occurred. Notice how we check NewProductArrived event for null. If no subscriber has subscribed for NewProductArrived event this would be null.  The call inside the if check is very important. Using NewProductArrived with brackets would invoke all the handlers which are methods subscribed to this event. (Remember we had learned in Delegates that delegate name with brackets invokes the methods?).

 private string ShopperName { get; set; }
        public Shopper(string shopperName)
        {
            this.ShopperName = shopperName;
        }
        public void RecieveNewProductNotification(object sender, NewProductEventArgs e)
        {
            Console.WriteLine("Shopper {0} thanks for subscribing to our notifications. New product {1} has arrived.", this.ShopperName, e.NewProduct);
        }
This class is the subscriber for the publisher mentioned above. The subscription is done in the form of the method RecieveNewProductNotification. Since the signature of this method fulfills the requirement of delegate EventHandler( object and NewProductEventArgs as paramters) so it acts as a delegate method for EventHandler.

static void Main(string[] args)
        {

           DepartmentStore departmentStore = new DepartmentStore();
           Shopper shopper1 = new Shopper("John");

           departmentStore.NewProductArrived += shopper1.RecieveNewProductNotification;
            departmentStore.NewProduct("Dishwasher");

            Shopper shopper2 = new Shopper("Mary");

            departmentStore.NewProductArrived += shopper2.RecieveNewProductNotification;
            departmentStore.NewProduct("Washing Machine");

            departmentStore.NewProductArrived -= shopper1.RecieveNewProductNotification;

            departmentStore.NewProduct("Furniture");

            Console.ReadLine();
        }
The main method shows how we publish the events. What I am doing with += and -= is subscribing and unsubscribing to the events.  The NewProduct() method in the DepartmentStore class fires or publishes the event NewProductArrived. All the subscribers are subscribed to the event with the lines departmentStore.NewProductArrived += shopper2.RecieveNewProductNotification;

Below is the output of the code (All of the above code should be simple to execute in a console application and is self explanatory).










The output above is interesting. Since the shopper John was already subscribed to the event so he was informed for the arrival of washing machine as well. But when he was unsubscribed with -= he was not informed about the Furniture. (Look carefully in the main method for the cause of the output).
            Events and delegates are powerful concepts provided in C# and are very handy when different “objects” of a system are interested in various “happenings” in the system.
Feel free to add more to this or rectify this. Happy coding!

7 comments:

  1. Very nice content specially Func and Action

    ReplyDelete
    Replies
    1. Hi Akhil apologies for the delayed reply and thanks for the nice comments! I usually don't write here anymore I am writing my articles on codeproject now so may be you can have a look there.
      Thanks a lot for your valuable comments!

      Delete
  2. This comment has been removed by the author.

    ReplyDelete
  3. This was really great for a beginner like me to get my head around these widely used concepts.
    Great work!

    ReplyDelete
  4. This was excellent article about delegate and event. Well explained.

    ReplyDelete
  5. Thanks Sanjeeva. I am no more active here, i am writing at codeproject now.

    ReplyDelete
  6. Excellent Article! After years of being in development I finally got my head around delegates! Thanks a bunch

    ReplyDelete