Click here to Skip to main content
15,867,686 members
Articles / Programming Languages / C#

Events and delegates in depth

Rate me:
Please Sign up or sign in to vote.
2.44/5 (13 votes)
30 Nov 2006CPOL5 min read 49.1K   27   13
This article describes events and delegates in detail. It also covers the .NET 2.0 features for delegates.

Introduction

While browsing through the Internet, I came across many articles on events and delegates. But I did not find any article which covered most of the contents under one roof. This is an attempt to do the same.

Many of us working on .NET 1.0 and .NET 1.1 have ideas about what delegates, events, multicast delegates etc. are. Still, I’ll give a brief idea here about all these for those who started their career with .NET 2.0 and C# 2.0.

Definition of a delegate

Delegates can be defined as type safe function pointers, and they are one of the six data types that can directly be declared inside namespaces. Type safe means a function which registers for a particular delegate is bound to have a similar signature as the delegate. Internally, delegates are treated as sealed classes with some predefined methods like BeginInvoke(), EndInvoke(), GetInvokationList(), etc. A delegate can be defined as:

C#
public delegate int AddDelegate(int a, int b);

Multicast delegates

C#
delegate void Del(string s);

class TestClass
{
    static void Hello(string s) { System.Console.WriteLine(" Hello, {0}!", s); } 
    static void Goodbye(string s) { System.Console.WriteLine(" Goodbye, {0}!", s); }

    static void Main()
    {
        Del a, b, c, d;
        // Create the delegate object a that references the method Hello: a = Hello; 
        // Create the delegate object b that references the method Goodbye: b = Goodbye; 
        // The two delegates, a and b, are composed to form c: c = a + b; 
        // Remove a from the composed delegate, leaving d, which calls only the method
        // Goodbye: d = c - a; 
        System.Console.WriteLine("Invoking delegate a:"); a("A");
        System.Console.WriteLine("Invoking delegate b:"); b("B");
        System.Console.WriteLine("Invoking delegate c:"); c("C");
        System.Console.WriteLine("Invoking delegate d:"); d("D");
    }
}

Multicast delegates can be defined with more than one element in its invocation list. I.e., they are delegates which are subscribed by more than one method. The above is a simple example from MSDN. If you are trying to use multicast delegates which return something, then the return value of the last registered function will be returned. MulticastDelegate is a special class. Compilers and other tools can derive from this class, but you cannot derive from it explicitly. The same is true of the Delegate class. A MulticastDelegate has a linked list of delegates, called an invocation list, consisting of one or more elements. When a multicast delegate is invoked, the delegates in the invocation list are called synchronously in the order in which they appear. If an error occurs during the execution of the list, then an exception is thrown.

Exceptions in multicast delegates

Suppose you add multiple delegates to a single multicast delegate. Each of these individual delegates must be invoked, regardless of whether an unhandled exception is thrown within one of the delegates. But, once a delegate in a multicast delegate throws an unhandled exception, no more delegates are fired. You need a way to trap unhandled exceptions within each individual delegate, while still allowing the rest of the delegates to fire. To avoid the breaking of the chain, you have to gracefully handle exceptions in all the functions. Use the GetInvocationList method. This method returns each individual delegate from a multicast delegate and, by doing so, allows you to invoke each delegate within the try block of an exception handler.

Anonymous methods in delegates

Sometimes, we want to put a very small code for calling a delegate. Creating functions for such a small code will make the code cumbersome, and difficult to read and debug. C# 2.0 comes with a new concept of anonymous methods. By using anonymous methods, you reduce the overhead in instantiating delegates by eliminating the need for a separate method. Here is a small example to illustrate this:

C#
AddDelegate add = delegate (int k) {return a + b;};

Covariance and Contravariance in delegates

These provide a degree of flexibility while matching delegate methods with delegate signatures.

Covariance

It permits a method with a derived return type to be used as a delegate. When a delegate method has a return type that is derived from the return type of the delegate, it is called as covariant. As the return type of the method is derived from the return type of the delegate, the type conversion is implicit. This enables us to create delegate methods that can be used by the base and the derived classes.

C#
public class BaseClass 
{
    // Some functions
}

public class DerivedClass : BaseClass { // Some functions }
public class MainClass
{
    // Define a delegate
    delegate BaseClass TestDelegate();
    private static DerivedClass ReturnDerived()
    { return new DerivedClass(); }
    public static void Main(string []args)
    { // Covariance allows this TestDelegate delg = ReturnDerived; }
}

Contravariance

It permits a method with derived parameters to be used as a delegate. When a delegate method signature has parameters that are derived from the delegate parameters, then the method is said to be contravariant.

C#
public class BaseClass
{   
    // Some functions 
}

public class DerivedClass : BaseClass { // Some functions }
public class MainClass
{
    // Define a delegate
    delegate BaseClass TestDelegate(BaseClass baseClassArg);
    private static DerivedClass ReturnDerived(DerivedClass dr)
    {
        return dr;
    }
    public static void Main(string []args)
    {
        // Contravariance allows this
        TestDelegate delg = ReturnDerived;
    }
}

We have seen what delegates and multicast delegates are, how to instantiate delegates, and covariance and contravariance. Delegates also support a strong type safe model for asynchronous programming by using methods like BeginInvoke() and EndVoke().

Now, we will see what events are.

Events

An event can be defined as “a member that enables an object or class to provide notifications”. Event types are used in connection with the Observer pattern. A collection of registered listeners or functions can be notified whenever an event occurs. Objects that are interested in receiving a notification of an event register a delegate instance with the event. An event is always defined with an associated delegate that has been defined and accessible. The event keyword is a delegate modifier. It must always be used in connection with a delegate. An event will contain a value as NULL if it does not have any registered listeners.

Examples include:

  • Button click event
  • Timer tick event
  • Page load event
  • Text change event of a textbox

Registering an event: We can register an event in the following way. Generally, if we have a closer look at the .NET event handlers, we see that the classes derived from EventArgs are input parameters for EventHandler delegates. We can have our customized class that can be derived from EventArgs, and can be used when an event is fired.

C#
public class MainClass
{
    private event AddDelegate AddEvent;
    public MainClass()
    {
        this.AddEvent += new AddDelegate(this.AddEventMethod);
    }
    public void ReiaseEvent()
    {
        if (AddEvent != null)
        {
            AddEvent(1, 2);
        }
    }

    private int AddEventMethod(int a, int b)
    {
        Console.WriteLine ("Sum is {0} + {1} = {2}", a, b, (a + b) );
    }
}

This way, you can register an event and raise an event. We can also have multiple functions registering for the same event. In this case, the += operator is used, and all registered functions are called one by one.

C#
this.AddEvent += new AddDelegate(this.AddFunction1);
this.AddEvent += new AddDelegate(this.AddFunction2);

If you want to remove any function from this chain, you can do like this:

C#
this.AddEvent -= new AddDelegate(this.AddFunction1);

The above code helps a lot when the function registering events is called more than once. In the event handling function, you can unregister the event.

Here is a simple test code:

C#
using System;
using System.Collections.Generic;
using System.Text;

namespace DelegatesEvents
{ 
    class Program 
    {
        static void Main(string[] args)
        {
            DelEveExample dev = new DelEveExample();
            if (args.Length >= 2)
            {
                Console.WriteLine(dev.CombineStrings(args[0], args[1]));
            }
            else 
            {
                Console.WriteLine("Please enter two arguments."); } Console.ReadLine();
            }
        }
    }

    delegate string StringConcatHandler(string str1, string str2);

    class DelEveExample
    {
        private event StringConcatHandler Handler;
        public DelEveExample()
        {
            this.Handler += new StringConcatHandler(DelEveExample_Handler);
        }
        internal string CombineStrings(string str1, string str2)
        { 
            if (str1.CompareTo(str2) != 0 && this.DelEveExample_Handler != null)
            {
                return this.DelEveExample_Handler(str1, str2);
            }
            return string.Empty;
        }
        private string DelEveExample_Handler(string str1, string str2)
        {
            string comStr = string.Empty;
            Console.WriteLine("Combining two strings...");
            Console.WriteLine("First string: {0}", str1);
            Console.WriteLine("Second string: {0}", str2);
            comStr = string.Concat(str1, str2);
            Console.WriteLine("After joining: {0}", comStr); return comStr;
        }
    }

Conclusion

Delegates are one of the most powerful features of .NET, and provides a strong, type safe model for event handling and asynchronous calling. Please let me know if I have missed anything.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Symantec Services, Pune
India India
Dear Friends,
I'm from Pune and currently working with Symantec. I'm having 7+ yrs of software development experience and I'm working on .NET since 6+ years.
I'm a Brainbench Certified Software Engineer in C#, ASP.NET, .NET Framework and ADO.NET.

Comments and Discussions

 
GeneralGood article Pin
Sujeet Bhujbal9-Mar-15 1:13
Sujeet Bhujbal9-Mar-15 1:13 
GeneralGood Article. Pin
santosh poojari15-May-07 2:34
santosh poojari15-May-07 2:34 
It helped me .thanks a lot.

Happy Coding
"San"




Generalintresting article but............ Pin
GaryWoodfine 31-Jan-07 12:04
professionalGaryWoodfine 31-Jan-07 12:04 
GeneralRe: intresting article but............ Pin
jdkulkarni31-Jan-07 16:57
jdkulkarni31-Jan-07 16:57 
GeneralOk Pin
NormDroid1-Dec-06 0:04
professionalNormDroid1-Dec-06 0:04 
GeneralRe: Ok Pin
jdkulkarni1-Dec-06 0:20
jdkulkarni1-Dec-06 0:20 
GeneralRe: Ok Pin
NormDroid1-Dec-06 0:24
professionalNormDroid1-Dec-06 0:24 
GeneralRe: Ok Pin
jdkulkarni1-Dec-06 0:44
jdkulkarni1-Dec-06 0:44 
GeneralRe: Ok Pin
NormDroid1-Dec-06 0:55
professionalNormDroid1-Dec-06 0:55 
GeneralRe: Ok Pin
Emilio Garavaglia1-Dec-06 6:04
Emilio Garavaglia1-Dec-06 6:04 
GeneralRe: Ok Pin
jdkulkarni3-Dec-06 17:19
jdkulkarni3-Dec-06 17:19 
GeneralRe: Ok Pin
Prakash Nadar2-Dec-06 6:00
Prakash Nadar2-Dec-06 6:00 
GeneralRe: Ok Pin
jdkulkarni3-Dec-06 17:31
jdkulkarni3-Dec-06 17:31 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.