Events and Delegates in Depth

Introduction

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

Many of us working on .NET 1.0 and .NET 1.1 have an idea about what delegates, events, multicast delegates, and so forth are. I'll give just a brief idea 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 one of the six data types that can directly be declared inside the namespaces. Type safe means the functions that are registered for a particular are bound to have similar signatures like a delegate. Internally, delegates are treated as sealed classes with some predefined methods such as BeginInvoke(), EndInvoke(), GetInvokationList(), and Method as property and so forth. A delegate can be defined as:

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

Instantiating a Delegate

Instantiating a delegate means creating an object of a delegate. An object of a delegate can be created by passing the address (name) of the function which is having similar signature like that of delegate. Here, you can instantiate a delegate. You have a function:

Private int AddMe(int a, int b);
AddDelegate add = new Add(AddMe);

Now I can call the AddMe() method by just doing

add(1, 2);

If you see the MSIL of a delegate, it is presented as a class that derives from System.MulticastDelegate, which in turn derives from System.Delegate. You also can use static method "Combine" of the Delegate class to combine more than one delegates.

Multicast Delegates

Multicast delegates can be defined as delegates that have more than one element in their invocation list. In other words, it is a delegate that is subscribed by more than one method.

Here is a simple example.

/************************************************************/
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");
}
}

Output
Invoking delegate a:
Hello, A!
Invoking delegate b:
Goodbye, B!
Invoking delegate c:
Hello, C!
Goodbye, C!
Invoking delegate d:
Goodbye, D!

/************************************************************/

If you try to use multicast delegates that return something, 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 execution of the list, an exception is thrown.

Exceptions in Multicast Delegates

Suppose you have added 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 breaking the chain, you have to gracefully handle exceptions in all 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, you want to use a very small amount of code to call 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 of separate method. Here is the small example to illustrate this.

/************************************************************/
     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

Covariance 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 delegate, it is called a covariant. Because the return type of method is derived from the return type of delegate, the type conversion is implicit. This enables you to create delegate methods that can be used by base and derived classes.

/************************************************************/
   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:

Contravariance 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, the method is said to be contravariant.

/************************************************************/
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;
   }
}
/************************************************************/

Events and Delegates in Depth

Generics in Delegates

In C# 2.0, strong generic support is also provided for delegates to make them safer. This is the simple example.

/************************************************************/
Public delegate void MyDelegate<T>(T item);
Public void Notify (int t) {}
MyDelegate<int> del = new MyDelegate<int> Nodify;
// Or
MyDelegate<int> del = Notify;
/************************************************************/

Thus, you have seen what delegates, multicast delegates, instantiating delegates, covariance, and contravariance are. Delegates also support a strong type safe model for asynchronous programming by using the methods like BeginInvoke() and EndVoke().

Now, you 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 value as NULL if it does not have any registered listeners.

An example can be given as:

  • Button click event
  • Timer tick event
  • Page load event
  • ext change event of textbox

Registering an event:

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

/************************************************************/
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 (b.Sum is {0} + {1} = {2}b., a,  b, (a + b) );
}
}
/************************************************************/

This way, you can register an event and raise an event. You 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.

/************************************************************/
    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.

/************************************************************/
    this.AddEvent -= new AddDelegate(this.AddFunction1);
/************************************************************/

The preceding 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 some simple test code.

/************************************************************/
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

You have seen what delegates, events, multicast delegates, covariance and contravariance are, raising an event under one roof. Delegates are one of the most powerful features of .NET and provide a strong, type safe model for event handling and asynchronous calling.



About the Author

Jayant Kulkarni

Dear Friends, I'm from Pune and curently working with Symantec Corp. I'm having more than 7 years of exp in software field and have worked on areas like ASP.NET, C#, .NET remoting, web services, pocket pc applciations. I'm a brainbench certified software engineer in .NET framework, C#, ADO.NET and ASP.NET. If you like any of my articles or you want to suggest some changes and improve the way I code, write articles feel free to mail me at: jayantdotnet@gmail.com

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • With JRebel, developers get to see their code changes immediately, fine-tune their code with incremental changes, debug, explore and deploy their code with ease (both locally and remotely), and ultimately spend more time coding instead of waiting for the dreaded application redeploy to finish. Every time a developer tests a code change it takes minutes to build and deploy the application. JRebel keeps the app server running at all times, so testing is instantaneous and interactive.

  • Download the Information Governance Survey Benchmark Report to gain insights that can help you further establish business value in your Records and Information Management (RIM) program and across your entire organization. Discover how your peers in the industry are dealing with this evolving information lifecycle management environment and uncover key insights such as: 87% of organizations surveyed have a RIM program in place 8% measure compliance 64% cannot get employees to "let go" of information for …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds