Thursday, January 17, 2008

Understanding Delegates

Understanding Delegates

A delegate is an important element of C# and is extensively used in every type of .NET application. A delegate is a class whose object (delegate object) can store a set of references to methods. This delegate object is used to invoke the methods. Many developers find delegates complicated. But believe me, it’s a simple concept to understand. In this article we will see how delegates work and in what situations they are used.
Let’s start with a simple program that shows how to declare and use a delegate.

class sample
{
                        delegate void del1();
                        delegate void del2 (int i);
                        public void fun()
                        {
                                     del1 d1 = new del1 (f1);
                                     d1();
                                     del2 d2 = new del2 (f2) ;
                                     d2 ( 5 ) ;
                        }
                        public void f1()
                        {
                                     Console.WriteLine (“Reached in f1”) ;
                        }
                        public void f2 (int i)
                        {
                                     Console.WriteLine (“Reached in f2”) ;
                        }
}

In the sample class we have declared two delegates—del1 and del2. The statement delegate void del1( ) ;
indicates that the delegate del1 is going to encapsulate methods, which takes no parameter and returns void. When this statement is encountered, a class del1 derived from a pre-defined class MultiCastDelegate gets created. Similarly, the statement delegate void del2 ( int i ) ; indicates that the delegate del2 will be used for methods taking one integer and returning a void. This statement would create another class del2 derived from the MultiCastDelegate class. The classes del1 and del2 are called ‘delegate classes’. To use the delegate class, like any other class, we have to declare its reference. We have declared the reference to delegate class del1 in the fun( ) method through the statement del1 d1 = new del1 ( f1 ) ;

This would create a delegate object d1. To the constructor of the delegate class we have passed the address of the method f1( ). C++ programmers may know that mentioning a method name without parentheses represents its address. The delegate object would now hold an address of the method f1( ). Next, we have called the f1( ) method using the delegate object d1. This is achieved using the statement d1( ) ;

If d1 is an object, how can we use it as if it is a method? Actually, the call d1( ) gets converted into a call to the d1.Invoke( ) method. The Invoke( ) method calls the f1( ) method using its address stored in delegate object. A delegate can contain references to multiple methods. In this case a single call to Invoke( ) calls all the methods.

Similar to d1 we have instantiated another delegate object d2 and stored in it an address of the method f2( ). As the method f2( ) takes an integer as a parameter we pave passed 5 to d2( ). The value 5 would get passed to all the methods whose references are stored in this delegate object. This makes it obvious that the signature of the delegate and that of the methods to be invoked using the delegate must be identical.

If we create an object of sample class and call the fun( ) method both the f1( ) and f2( ) methods would get called through d1 and d2 respectively.

Delegates make possible calling of methods using reference to methods the object oriented way. This avoids using any complex technique like pointers to functions.

Delegates in Inheritance
Suppose there is a base class and a class derived from this class. A method is invoked from a base class method using a delegate. We can initialise this delegate in derived class so that when the method is invoked from the base class it is the derived class’s method that would get called. Here is the program that works similar to this situation.

using System;
 
namespace delegateinherit
{
                        public delegate void del1();
                        class mybase
                        {
                                     public del1 d;
                                     public void fun()
                                     {
                                                 d();
                                     }
                        }
                        class der : mybase
                        {
                                     public der()
                                     {
                                                 d = new del1 (f1);
                                                 fun();
                                                 d = new del1 (f2);
                                                 fun();
                                     }
                                     public void f1()
                                     {
                                                 Console.WriteLine (“Reached in f1”) ;
                                     }
                                     public void f2()
                                     {
                                                 Console.WriteLine (“Reached in f2”) ;
                                     }
                        }
                        class Class1
                        {
                                     static void Main (string[] args)
                                     {
                                                 der d = new der();
                                     }
                        }
}

Here, from the fun( ) method of the mybase class we have used the delegate object d to invoke a method. The fun( ) method is called twice from the constructor of the derived class der. Before calling fun( ) method, we have associated the delegate d, first with method f1( ) and then with f2( ). Thus, if we create an object of the der class, first the f1( ) and then f2( ) method get called. We can invoke both the methods in a single call to fun( ) as shown below.

d = new del1 ( f1 ) ;
d += new del1 ( f2 ) ;
fun( ) ;

Where Delegates are useful
In a general WinForm application, a class Form1 gets created, which is derived from the Form class. If we want to handle mouse click messages, we override the OnMouseDown( ) message handler in the Form1 class. When we click the mouse button, control reaches to the OnMouseDown( ) of the derived class i.e Form1 class because of the simple rule of inheritance. But if we click on a push button on the form, the OnMouseDown( ) of the Form1 class does not get called. Because Form1 class is not derived from the class that creates button. In such a situation, delegates prove useful. Calling methods using delegate require only that the signature of methods and that of the delegate should match. It does not require any inheritance chain to call the appropriate method. So, in .NET, all the event handlers are called through delegates, instead of relying on the inheritance chain.

Delegate at work
As said earlier, all the event handlers are called using delegates. We will now see a dummy program to explain how the Paint event handler must be getting called using a delegate.

using System;
namespace delegateatwork
{
   public delegate void PaintHandler();
   class Form
   {
                        public PaintHandler Paint ;
                        public void OnPaint()
                        {
                                     Paint();
                        }
   }
   class form1 : Form
   {
                        public form1()
                        {
                                     Paint += new PaintHandler (form1_Paint);
                                     OnPaint() ;
                        }
                        public void form1_Paint()
                        {
                                     Console.WriteLine (“form1_Paint”);
                        }
            }
   class Class1
   {
                        static void Main (string[] args)
                        {
                                     form1 f = new form1();
                        }
   }
}

We have designed a class Form1 that contains a method called Form1_Paint( ). The Form1 class is derived from the Form class. In the Form class we have declared an object Paint of the delegate class PaintHandler. In Main( ) we have instantiated an object of the Form1 class. This results in its constructor being called. In the constructor of the Form1 class we have initialised the Paint object and encapsulated in it the Form1_Paint( ) method. Next we have called the OnPaint( ) method of the Form class (in WinForm application Paint event gets generated automatically), which in turn calls the Form1_Paint( ) method via Paint.

1 Comment:

Unknown said...

Very Simple and clear explanation.