Delegates In .Net

Overview

All of us have been exposed to event driven programming of some sort or the other. C# adds on value to the often mentioned world of event driven programming by adding support through events and delegates. The emphasis of this article would be to identify what exactly happens when you add an event handler to your common UI controls. A simple simulation of what could possibly be going on behind the scenes when the AddOnClick or any similar event is added to the Button class will be explained. This will help you understand better the nature of event handling using multi cast delegates.

Delegates

A delegate in C# is similar to a function pointer in C or C++. Using a delegate allows the programmer to encapsulate a reference to a method inside a delegate object. The delegate object can then be passed to code which can call the referenced method, without having to know at compile time which method will be invoked.
In most cases, when we call a function, we specify the function to be called directly. If the class MyClass has a function named Process, we'd normally call it like this (SimpleSample.cs):
using System;

namespace Akadia.NoDelegate
{
    public class MyClass
    {
        public void Process()
        {
            Console.WriteLine("Process() begin");
            Console.WriteLine("Process() end");
        }
    }

    public class Test
    {
        static void Main(string[] args)
        {
            MyClass myClass = new MyClass();
            myClass.Process();
        }
    }
}
That works well in most situations. Sometimes, however, we don't want to call a function directly - we'd like to be able to pass it to somebody else so that they can call it. This is especially useful in an event-driven system such as a graphical user interface, when I want some code to be executed when the user clicks on a button, or when I want to log some information but can't specify how it is logged.
An interesting and useful property of a delegate is that it does not know or care about the class of the object that it references. Any object will do; all that matters is that the method's argument types and return type match the delegate's. This makes delegates perfectly suited for "anonymous" invocation.
The signature of a single cast delegate is shown below:
delegate result-type identifier ([parameters]);
where:
  • result-type: The result type, which matches the return type of the function.
  • identifier: The delegate name.
  • parameters: The Parameters, that the function takes.
Examples:
public delegate void SimpleDelegate ()
This declaration defines a delegate named SimpleDelegate, which will encapsulate any method that takes
no parameters and returns no value.
public delegate int ButtonClickHandler (object obj1, object obj2)
This declaration defines a delegate named ButtonClickHandler, which will encapsulate any method that takes
two objects as parameters and returns an int.
A delegate will allow us to specify what the function we'll be calling looks like without having to specify which function to call. The declaration for a delegate looks just like the declaration for a function, except that in this case, we're declaring the signature of functions that this delegate can reference.
There are three steps in defining and using delegates:
  • Declaration
  • Instantiation
  • Invocation
A very basic example (SimpleDelegate1.cs):
using System;

namespace Akadia.BasicDelegate
{
    // Declaration
    public delegate void SimpleDelegate();

    class TestDelegate
    {
        public static void MyFunc()
        {
            Console.WriteLine("I was called by delegate ...");
        }

        public static void Main()
        {
            // Instantiation
            SimpleDelegate simpleDelegate = new SimpleDelegate(MyFunc);

            // Invocation
            simpleDelegate();
        }
    }
}
Compile an test:
# csc SimpleDelegate1.cs
# SimpleDelegate1.exe
I was called by delegate ...
For our next, more advanced example (SimpleDelegate2.cs), declares a delegate that takes a single string parameter and has no return type:
using System;

namespace Akadia.SimpleDelegate
{
    // Delegate Specification
    public class MyClass
    {
        // Declare a delegate that takes a single string parameter
        // and has no return type.

        public delegate void LogHandler(string message);

        // The use of the delegate is just like calling a function directly,
        // though we need to add a check to see if the delegate is null
        // (that is, not pointing to a function) before calling the function.

        public void Process(LogHandler logHandler)
        {
            if (logHandler != null)
            {
                logHandler("Process() begin");
            }

            if (logHandler != null)
            {
                logHandler ("Process() end");
            }
        }
    }

    // Test Application to use the defined Delegate
    public class TestApplication
    {
       
// Static Function: To which is used in the Delegate. To call the Process()
        // function, we need to declare a logging function: Logger() that matches
        // the signature of the delegate.

        static void Logger(string s)
        {
            Console.WriteLine(s);
        }

        static void Main(string[] args)
        {
            MyClass myClass = new MyClass();

            // Crate an instance of the delegate, pointing to the logging function.
            // This delegate will then be passed to the Process() function.

            MyClass.LogHandler myLogger = new MyClass.LogHandler(Logger);
            myClass.Process(myLogger);
        }
    }
}
Compile an test:
# csc SimpleDelegate2.cs
# SimpleDelegate2.exe
Process() begin
Process() end
In the simple example above, the Logger( ) function merely writes the string out. A different function might want to log the information to a file, but to do this, the function needs to know what file to write the information to (SimpleDelegate3.cs)
using System;
using System.IO;

namespace Akadia.SimpleDelegate
{
    // Delegate Specification
    public class MyClass
    {
        // Declare a delegate that takes a single string parameter
        // and has no return type.

        public delegate void LogHandler(string message);

        // The use of the delegate is just like calling a function directly,
        // though we need to add a check to see if the delegate is null
        // (that is, not pointing to a function) before calling the function.

        public void Process(LogHandler logHandler)
        {
            if (logHandler != null)
            {
                logHandler("Process() begin");
            }

            if (logHandler != null)
            {
                logHandler ("Process() end");
            }
        }
    }

    // The FileLogger class merely encapsulates the file I/O
    public class FileLogger
    {
        FileStream fileStream;
        StreamWriter streamWriter;

        // Constructor
        public FileLogger(string filename)
        {
            fileStream = new FileStream(filename, FileMode.Create);
            streamWriter = new StreamWriter(fileStream);
        }

        // Member Function which is used in the Delegate
        public void Logger(string s)
        {
            streamWriter.WriteLine(s);
        }

        public void Close()
        {
            streamWriter.Close();
            fileStream.Close();
        }
    }

    // Main() is modified so that the delegate points to the Logger()
    // function on the fl instance of a FileLogger. When this delegate
    // is invoked from Process(), the member function is called and
    // the string is logged to the appropriate file.

    public class TestApplication
    {
        static void Main(string[] args)
        {
            FileLogger fl = new FileLogger("process.log");

            MyClass myClass = new MyClass();

            // Crate an instance of the delegate, pointing to the Logger()
            // function on the fl instance of a FileLogger.

            MyClass.LogHandler myLogger = new MyClass.LogHandler(fl.Logger);
            myClass.Process(myLogger);
            fl.Close();
        }
    }
}
The cool part here is that we didn't have to change the Process() function; the code to all the delegate is the same regardless of whether it refers to a static or member function.
Compile an test:
# csc SimpleDelegate3.cs
# SimpleDelegate3.exe
# cat process.log
Process() begin
Process() end
Being able to point to member functions is nice, but there are more tricks you can do with delegates. In C#, delegates are multicast, which means that they can point to more than one function at a time (that is, they're based off the System.MulticastDelegate type). A multicast delegate maintains a list of functions that will all be called when the delegate is invoked. We can add back in the logging function from the first example, and call both delegates. Here's what the code looks like:
using System;
using System.IO;

namespace Akadia.SimpleDelegate
{
    // Delegate Specification
    public class MyClass
    {
        // Declare a delegate that takes a single string parameter
        // and has no return type.

        public delegate void LogHandler(string message);

        // The use of the delegate is just like calling a function directly,
        // though we need to add a check to see if the delegate is null
        // (that is, not pointing to a function) before calling the function.

        public void Process(LogHandler logHandler)

        {
            if (logHandler != null)
            {
                logHandler("Process() begin");
            }

            if (logHandler != null)
            {
                logHandler ("Process() end");
            }
        }
    }

    // The FileLogger class merely encapsulates the file I/O
    public class FileLogger
    {
        FileStream fileStream;
        StreamWriter streamWriter;

        // Constructor
        public FileLogger(string filename)
        {
            fileStream = new FileStream(filename, FileMode.Create);
            streamWriter = new StreamWriter(fileStream);
        }

        // Member Function which is used in the Delegate
        public void Logger(string s)
        {
            streamWriter.WriteLine(s);
        }


        public void Close()
        {
            streamWriter.Close();
            fileStream.Close();
        }
    }

   
// Test Application which calls both Delegates
    public class TestApplication
    {
        
// Static Function which is used in the Delegate
        static void Logger(string s)
        {
            Console.WriteLine(s);
        }


        static void Main(string[] args)
        {
            FileLogger fl = new FileLogger("process.log");

            MyClass myClass = new MyClass();

            // Crate an instance of the delegates, pointing to the static
            // Logger() function defined in the TestApplication class and
            // then to member function on the fl instance of a FileLogger.

            MyClass.LogHandler myLogger = null;
            myLogger += new MyClass.LogHandler(Logger);
            myLogger += new MyClass.LogHandler(fl.Logger);


            myClass.Process(myLogger);
            fl.Close();
        }
    }
}

Compile an test:
# csc SimpleDelegate4.cs
# SimpleDelegate4.exe
Process() begin
Process() end

# cat process.log
Process() begin
Process() end

...............................................................................................................................................

I noticed that you arrived here from Google looking for delegates in .net. The below article is probably best suited for you based on the provided keywords. However to aid you on your search here's a list of related blog posts.

  • Fun with Func<T,TResult> Delegates, Events and Async Operations read more
  • MAB ContainerModel / Funq: a transparent container read more
  • Getting the App’s Startup Assembly in WPF Designer? read more
  • MAB ContainerModel / Funq: a transparent container read more
  • Death to .NET Events read more
  • New Development Snapshot read more
  • Tech.Ed New Zealand T-2 Weeks read more
  • SQLAuthority News Summary of TechEd India 2009 A Grand Event read more
  • Death to .NET Events read more
  • Just got back from Tech Ed Aus 2009 read more
If the information provided on this page is not helpful, please consider posting in our forums or in the comment section of this page. Registration takes less than 30 seconds.

Contents [hide]

What is a Delegate?

When we talk about delegates in .NET then one thing that comes to our mind is what delegate means to a novice developer. In simple words we can say delegates are a .NET object which points to a method that matches its specific signature.
In other words delegates are function pointers that point to function of matching signatures. Function pointers which are extensively used in c/c++ to points to a function holds only the memory address of the function, it doesn’t carry further information about the function parameters, return type etc. On the other hand .NET framework has introduced a type-safe mechanism called delegates, with automatic verification of the signature by the compiler.
So comparatively delegates add a safety dimension in handling function pointers in .NET.
So we can say that delegates are type-safe, object oriented, secure .NET objects which can be used to invoke methods of matching signature.
While using delegates it is very much necessary to make sure that the functions which the delegates points has the same number of argument type and same return type. For example if we have a method that takes a single string as a parameter and another method that takes two string parameters, then we need to have two separate delegate type for each method.

Types of Delegate

Delegates are of two types:

Single cast delegate

A delegate is called single cast delegate if it invokes a single method. In other words we can say that SingleCast Delegates refer to a single method with matching signature. SingleCast Delegates derive from the System.Delegate class
SingleCast Delegates can be defined using the following syntax:
1.public  delegate int mySingleCastDelegate(int iFirstargument, int iSecondArgument);
In the above code I have defined a delegate which can encapsulate a method that takes two parameters of type integer and then returns an integer as a return type.
Let’s look at an example of SingleCast Delegates to see their implementation.

Listing 1: Single cast delegate example

01.using System;
02.namespace SingleCastDelegateDemoApplication
03.{   
04.    class mySingleDelegateClass
05.    {
06.        public  delegate int mySingleCastDelegate(int iFirstargument,
07.            int iSecondArgument);      
08.        static void Main(string[] args)
09.        {
10.            mySingleDelegateClass clsSingleCastDelegate = new mySingleDelegateClass();
11.          mySingleCastDelegate singleCastMaxNumberDelegate =
12.             new mySingleCastDelegate(clsSingleCastDelegate.myMaxFunction);
13.            int iMaxNumberResult = singleCastMaxNumberDelegate(10, 20);
14.            Console.WriteLine(@"Result by calling the myMaxFunction method using a
15.               delegate: {0}", iMaxNumberResult);
16.            Console.Read();
17.        }
18.        public int myMaxFunction(int iFirstNumber, int iSecondNumber)
19.        {
20.            if (iFirstNumber > iSecondNumber)
21.                return iFirstNumber;
22.            else
23.                return iSecondNumber;
24.        }
25.    }
26.}

Explanation

In the above code snippet I have declared a single delegate which takes two integer type as arguments and returns an integer as return type.
1.delegate int mySingleCastDelegate(int iFirstargument, int iSecondArgument); 
At runtime I have created a delegate variable as singleCastMaxNumberDelegate of type mySingleCastDelegate. Using the delegate variable, I can point to any method that has the matching signature.
In the above example the method myMaxFunction has the matching signature with the delegate variable. So using the new keyword I have referenced the delegate variable to the myMaxFunctionf:
1.mySingleCastDelegate singleCastMaxNumberDelegate =
2.   new mySingleCastDelegate(clsSingleCastDelegate.myMaxFunction);
Now I can call the function myMaxFunction by passing required parameters through the delegate.
1.int iMaxNumberResult = singleCastMaxNumberDelegate(10, 20);
When we run the above code snippet, we get the result as follows:

Figure 1: Single cast delegate result

Single cast delegate result

Multicast Delegates

MultiCast Delegates are nothing but a single delegate that can invoke multiple methods of matching signature. MultiCast Delegate derives from System.MulticastDelegate class which is a subclass of System.Delegate.
In Multi-Casting basically we create a single delegate that in turn invokes multiple encapsulated methods. We can use MultiCast Delegates when multiple calls to different methods are required. For example if we are required to call two methods on a single button click event or mouse over event then using MultiCast Delegates we can easily call the methods.
The System.MulticastDelegate class provides required methods to play with delegates. There are two methods, Combine and Remove, which are used to play with delegates.
The Combine method is a static method of System.MulticastDelegate class which is used to combine the delegates and the Remove method is used to remove the delegate from the list.
The Combine method takes an array of delegate as a parameter and returns a new delegate that represents the combination of all the delegates in the array.
MultiCast Delegate can have arguments and can have return values as well. The only things is that the methods pointed by delegate needs to have same return type as that of the return type of delegate.
In the below code snippet I have defined a delegate which can act as a multicast delegate to encapsulate methods that takes two parameter as of type integer and returns nothing.
1.public delegate void MyMulticastDelegate(int p, int q);
Let’s take an example of MultiCast Delegate to see how multiple methods can be called.

Listing 2: Multi cast delegate example

01.using System;
02.namespace MultiCastDelegatesDemoapplication
03.{
04.    class MultiCastDelegatesClass
05.    {
06.        //delegate declaration
07.        Public delegate void MyMulticastDelegate(int p, int q);
08.        static void Main(string[] args)
09.        {
10.            MultiCastDelegatesClass clsMultiCastDelegate =
11.               new MultiCastDelegatesClass();
12.            MyMulticastDelegate myDelegate = null;
13.            MyMulticastDelegate myMultiCastDelegateAddition =
14.               new MyMulticastDelegate(clsMultiCastDelegate.myAddtionfunction);
15.            MyMulticastDelegate myMultiCastDelegateMaxNumber =
16.               new MyMulticastDelegate(clsMultiCastDelegate.myMaxFunction);
17.            Console.WriteLine("Used Combine function to bind the delegates... ");
18.myDelegate = (MyMulticastDelegate)System.Delegate.Combine(myMultiCastDelegateAddition,
19.   myMultiCastDelegateMaxNumber);
20.            //call made using the multicast delegate
21.            myDelegate(10, 23);
22.            Console.WriteLine();
23.            Console.WriteLine(@"Used Remove function to remove the delegate from
24.               the list.. ");
25.            myDelegate = (MyMulticastDelegate)System.Delegate.Remove(myDelegate,
26.               myMultiCastDelegateMaxNumber);
27.            myDelegate(10, 23);
28.            Console.Read();
29.        }
30.        //function calculates the sum of passed arguments
31.        public void myAddtionfunction(int iFirstNumber, int iSecondNumber)
32.        {
33.            int result = iFirstNumber + iSecondNumber;
34.            Console.WriteLine(@"In myAddtionfunction called by a multi cast delegate:
35.               Result is :" + result);
36.        }
37.        //function checks the max value of passed arguments
38.        public void myMaxFunction(int iFirstNumber, int iSecondNumber)
39.        {
40.            int max ;
41.            if (iFirstNumber > iSecondNumber)
42.            {
43.                max = iFirstNumber;
44.            }
45.            else
46.            {
47.                max = iSecondNumber;             
48.            }
49.            Console.WriteLine(@"In myMaxFunction called by a multi cast delegate:
50.               Result is :" + max);
51.        }
52.    }
53.}

Code Explanation

There are two functions declared, myAddtionfunction and myMaxFunction. Both of theses functions take two integer type as parameters and return void. I have created three delegate variables of type MultiCast Delegate, out of which myDelegate assigned with null value where as the other two delegates referenced to each of two functions.
1.MyMulticastDelegate myDelegate = null;
2.MyMulticastDelegate myMultiCastDelegateAddition =
3.   new MyMulticastDelegate(clsMultiCastDelegate.myAddtionfunction);
4.MyMulticastDelegate myMultiCastDelegateMaxNumber =
5.   new MyMulticastDelegate(clsMultiCastDelegate.myMaxFunction
I have used the Combine method of system.delegate to combine the two delegate variables.
1.myDelegate = (MyMulticastDelegate)System.Delegate.Combine(myMultiCastDelegateAddition,
2.    myMultiCastDelegateMaxNumber);
System.Delegate provides another method, remove, which can be used to remove the specific delegate from the list. Here the remove function is being used to remove the myMultiCastDelegateMaxNumber function from myDelegate list
1.myDelegate = (MyMulticastDelegate)System.Delegate.Remove(myDelegate,
2.   myMultiCastDelegateMaxNumber);
When we run the above code snipet, then we get the following result:

Figure 2: Multicast Delegate example

Multicast Delegate example

Event handling using delegates

In a sense we can say that an event is a resultant outcome of an action. In programming terms, when a user does some actions on a User Interface such as clicking a button or hover a mouse on an image then some things happen on the user interface, like some pop up opens up or the page posts back to server etc. In this sense if clicking a button is an action then the outcome of that action is an event.
In the .NET Framework an event enables objects in a class to notify other objects that an action has been performed and that they should react. Events in the .NET Framework are based on the publisher-subscriber model. Publisher of the event is the class that publishes an event and the subscriber of the event is the class that registers to the published event. In C#, any object can publish a set of events to which other applications can subscribe. When the publishing class raises an event, all the subscribed applications are notified.
In the event communication process the publisher doesn’t know who is going to subscribe the event. Under this circumstance delegates play a handy role to act as an intermediary between the publisher and the subscriber.
The following example describes the methodology by which delegates can be used in event handling in C#.NET.

Listing 3: Event handling using delegate example

01.using System;
02.namespace DelegatesEventHandling
03.
04.    class MyDelegateClass
05.    {
06.        //MyFirstEventHandler is the delegate for the Demo event.
07.        public delegate void MyFirstEventHandler(object sender, MyDemoEventArgs e);
08.        //Myevent is the event of type MyFirstEventHandler delegates.
09.        public event MyFirstEventHandler MyEvent;
10.        //This function will create the MyDemoEventArgs and then raise the event.
11.        public void FireMyFirstDemoEvent(string strMyEventArgs)
12.        {
13.            MyDemoEventArgs objEventArgs = new MyDemoEventArgs(strMyEventArgs);
14.            if (MyEvent != null)
15.            {
16.                //Raise the event by invoking the delegate.
17.                //The call must match the signature of MyFirstEventHandler.
18.                MyEvent(this, objEventArgs);
19.            }
20.        }
21.    }
22.    //Event handler class
23.    class MyEventHandlerClass
24.    {
25.        //This function creates the delegate and raise the demo events.
26.        public void RaiseMyDemoEvent(MyDelegateClass objDelegate)
27.        {
28.            objDelegate.MyEvent +=
29.               new MyDelegateClass.MyFirstEventHandler(MyEventHandlerFunction);
30.        }
31.        //Method to be executed when the event occurs.
32.        public void MyEventHandlerFunction(object sender, MyDemoEventArgs e)
33.        {
34.            Console.WriteLine("Event handling process started...");
35.            Console.WriteLine("Inside MyEventHandlerFunction...");
36.            Console.WriteLine("My Demo Event Contains argument : {0}",
37.               e.strMyEventArgs);
38.            Console.ReadLine();
39.        }
40.    }
41.    //MyDemoEventArgs is the class that holds event data for the Demo event.
42.    //It derives from the base class for event data, EventArgs.
43.    public class MyDemoEventArgs : EventArgs
44.    {
45.        public string strMyEventArgs;
46.        public MyDemoEventArgs(string strMyEventArgs)
47.        {
48.            this.strMyEventArgs = strMyEventArgs;
49.        }
50.     }
51.    class EventProcess
52.    {
53.        static void Main(string[] args)
54.        {
55.            MyDelegateClass objDelegate = new MyDelegateClass();
56.            MyEventHandlerClass myEventHandler = new MyEventHandlerClass();
57.            myEventHandler.RaiseMyDemoEvent(objDelegate);
58.            objDelegate.FireMyFirstDemoEvent("Demo event");                  
59.        }
60.    }
61.}

Explanation

In the MyDelegateClass I have used the following line to declare the event handler which is nothing but a delegate having two parameters: (object sender, MyEventArgs e).
1.public delegate void MyFirstEventHandler(object sender, MyDemoEventArgs e);
The first parameter denotes the object which is the source of the event; that is, the publishing object. The second parameter is an object derived from MyDemoEventArgs class which containes information about the event data.
The class MyDemoEventArgs is derived from the class EventArgs. EventArgs is the base class of more event specialized classes containing event data, like MouseEventArgs, NavigateEventArgs, etc. In a GUI event we can use the objects of these specified EventArgs class where as for a non GUI event we need to create our own customized EventArgs class to hold the data that can be passed to the delegate object.
The following code is used to create our own customized EventArgs class.
1.public class MyDemoEventArgs : EventArgs
2.{
3.    public string strMyEventArgs;
4.    public MyDemoEventArgs(string strMyEventArgs)
5.    {
6.        this.strMyEventArgs = strMyEventArgs;
7.    }
8.}
Next I have declared a public event called MyEvent of type MyFirstEventHandler delegate.
1.public event MyFirstEventHandler MyEvent;
The function FireMyFirstDemoEvent(string strMyEventArgs) is the entry point of our event. This function creates the EventArgs instance and then raises the event by passing the EventArgs object.
01.//This function will create the MyDemoEventArgs and then raise the event.
02.public void FireMyFirstDemoEvent(string strMyEventArgs)
03.{
04.   MyDemoEventArgs objEventArgs = new MyDemoEventArgs(strMyEventArgs);
05.   if (MyEvent != null)
06.   {
07.       //Raise the event by invoking the delegate.
08.       //The call must match the signature of MyFirstEventHandler.
09.       MyEvent(this, objEventArgs);
10.   }
11.}
The method RaiseMyDemoEvent(MyDelegateClass objDelegate) present in the MyEventHandlerClass adds a delegate containing the method MyEventHandlerFunction(object sender, MyDemoEventArgs e) to the class event. When the event is raised it will subsequently execute MyEventHandlerFunction() method.
01.//This function creates the delegate and raise the demo events.
02.public void RaiseMyDemoEvent(MyDelegateClass objDelegate)
03.{
04.   objDelegate.MyEvent +=
05.      new MyDelegateClass.MyFirstEventHandler(MyEventHandlerFunction);
06.}
07.//Method to be executed when the event occurs.
08.public void MyEventHandlerFunction(object sender, MyDemoEventArgs e)
09.{
10.   Console.WriteLine("Event handling process started...");
11.   Console.WriteLine("Inside MyEventHandlerFunction...");
12.   Console.WriteLine("My Demo Event Contains argument : {0}", e.strMyEventArgs);
13.   Console.ReadLine();
14.}
In the main function I have created the instances of the class that fires the event and an instance of the class that handles the event.
01.public class EventProcess
02.{
03.   static void Main(string[] args)
04.   {
05.       MyDelegateClass objDelegate = new MyDelegateClass();
06.       MyEventHandlerClass myEventHandler = new MyEventHandlerClass();
07.       myEventHandler.RaiseMyDemoEvent(objDelegate);
08.       objDelegate.FireMyFirstDemoEvent("Demo event");                  
09.   }
10.}
When we run the sample code snippet, we get the following result:

Figure 3: Event Handling example

Event Handling example

Conclusion

In my conclusion I can say that Delegates are an important feature of .NET with respect to handling events. If you are using Visual Basic .NET then it is not required at all to create delegates while dealing with events because With Events and AddHandler keywords take care of all the details under the hoods. Also, Delegates play a useful role in situations where you need to execute a particular action without knowing the method you would be calling upon to execute the action.

0 comments:

Post a Comment