The Mysterious Delegates

If you google out for Delegates you will find dozens of article describing how to use them, but hardly few describe the basic reason for their existence. Let's dig that out from the history.


What are Delegates?


A "Delegate" can be perceived as a pointer to a method(or more than one methods in the case of Multicast Delegates) whose arguments(number and type) and return type matches with the method they point to.
It's the responsibility of the Delegate to call the method at run-time. At compile time the code doesn't even know which method is going to be called.
In short they are reference type with the address of a method.
Thus the actual method call is hidden from the caller.

A simple example:-

public partial class TestDelegate : System.Web.UI.Page
{
    public delegate int CalcOperations(int i, int j);
    public class Operations
    {
        public static int Add(int i, int j)
        {
            return (i + j);
        }
        public static int Sub(int i, int j)
        {
            return (i - j);
        }
    }
    protected void Page_Load(object sender, EventArgs e)
    {
        CalcOperations operation = new CalcOperations(Operations.Add);
        int result = operation.Invoke(2, 3);
    }
} 

Why we need them?

  1. To delay the method call to run-time and hide the actual call from the caller.(This has already been made clear from the above example.)
  2. To make Asynchronous calls.
  3. If used properly they improve the performance.
  4. Used for defining an Event.
  5. For carrying an array of methods and calling the sequence, using Multicast Delegates.
  6. Implementing Callback notification.
  7. Anonymous method calls. 

How Delegates benefit us? 

Let's try to understand each of  the above points in detail:-

1. Asynchronous Calls 
Consider the below example code

namespace Learnings
{
    delegate int CalcOperation(int x, int y);
    class Program
    {
        static void Main(string[] args)
        {
            CalcOperation operation = new CalcOperation(Sum);
            Console.WriteLine("Invoking..");
            operation(10, 20);
            Console.WriteLine("Main method in process again");
            Console.WriteLine("End Main");
            Console.ReadLine();
        }
        static void Sum(int x, int y)
        {
            Console.WriteLine("Invocation in Progress..");
            Thread.Sleep(2000);
            Console.WriteLine("Result is: " + (x + y));
        }
    }
}

In the above code we are invoking the delegate but the method called makes the thread Sleep.
Since it's a synchronous call the Main method waits till the method Sum finishes Sleep. Hence the output is:-
Invoking..
Invocation in Progress..
-----------------------------------> At this point the console waits.
Result is: 30
Main method in process again
End Main


Now let's see how the Asynchronous call using Delegates makes the Main method continue with it's work.
Let's keep rest of the code as it is and modify the Main

static void Main(string[] args)
{
        CalcOperation operation = new CalcOperation(Sum);
        Console.WriteLine("Invoking..");
        IAsyncResult asyncResult = operation.BeginInvoke(10, 20, null, null);
        Console.WriteLine("Main method in process again");
        Console.WriteLine("End Main");
        Console.ReadLine();
}


Run this modified code and see the output. It is:

Invoking..
Main method in process again
End Main
Invocation in Progress..
-----------------------------------> At this point the console waits.
Result is: 30

Wow!!...I was able to finish all the job done on main without waiting for the method Sum to finish. Interestingly Delegate took the responsibility to throw the result when the processing in Sum was over.
This is what we call Asynchronous operation.

Also note that you can anytime have the third modification of the Main method. It's given below:-

static void Main(string[] args)
{
        CalcOperation operation = new CalcOperation(Sum);
        Console.WriteLine("Invoking..");
        IAsyncResult asyncResult = operation.BeginInvoke(10, 20, null, null);
        Console.WriteLine("Main method in process again");
        operation.EndInvoke(asyncResult);
        Console.WriteLine("End Main");
        Console.ReadLine();
}

The output here is:
Invoking..
Main method in process again
Invocation in Progress..
-----------------------------------> At this point the console waits.
Result is: 30
End Main

That means till the time we call EndInvoke we can do any processing in between but I will call this too a sort of Synchronous(Open for Reader's comments..).

2. Performance Improvement (if properly used)

Please note that delegates are reference type and immutable. Hence too many operations on a single delegate object might end up in creating multiple copies, and performance degradation. So it should be used judiciously.
Also due to the Asynchronous calls you can continue processing with waiting for other operations to finish and hence saving time.

3. Defining Events

I will try explaining things with the below code where I try creating an Event fired for Calculation of Sum.
First we will create the event class

namespace Learnings
{
    public delegate void CalcDelegate(int x, int y);
    public class Event
    {
        public event CalcDelegate CalcEvent;
        public Event()
        {
            //Attaching Delegates to Event
            CalcEvent += new CalcDelegate(OnStartCalc);
            //Calling Event
            CalcEvent(10, 20);
        }
        public void OnStartCalc(int x, int y)
        {
            Console.WriteLine("Sum is: " + (x + y));
            Console.Read();
        }
    }
}


Note: The event declaration is a must inside the event class only.
Now we will call the event from the Main method.

namespace Learnings
{
    class Program
    {
        static void Main(string[] args)
        {
            Event objEvent = new Event();
        }
    }
}

There are numerous other modifications to event usage. I have presented the simplest example for easy understanding.

4. Multicast Delegates for calling methods in sequence

First we will understand the Multicast delegate through a sample code.

namespace Learnings
{
    delegate void CalcOperation(int x, int y);
    class Program
    {
        static void Main(string[] args)
        {
            CalcOperation operation = new CalcOperation(Sum);
            operation += new CalcOperation(Sub);
            operation += new CalcOperation(Mult);
            //executing the delegates
            operation(20, 10);
            Console.Read();
        }
        static void Sum(int x, int y)
        {
            Console.WriteLine("Addition Result is: " + (x + y));
        }
        static void Sub(int x, int y)
        {
            Console.WriteLine("Subtraction Result is: " + (x - y));
        }
        static void Mult(int x, int y)
        {
            Console.WriteLine("Multiplication Result is: " + (x * y));
        }
    }
}

Note: Multicast delegates should not have return type as they will return the value of the last executed method and the output values of other methods will be lost.

There is also a nice article from MSDN which explains the operations on delegates. 

Comments

Popular Posts