If you take a look at the following line, you will probably recognize it as the signature for an event handler.

1 public void Handler( object sender, EventArgs e ) {

The reason why event handlers are defined with this signature is because it matches the signature of the EventHandler delegate which looks like this:

1 public delegate void EventHandler(object sender, EventArgs e);

So when you’re registering an event handler to an event. You’re adding a delegate to the internal list of delegates. (Think of a type safe array of function pointers.) So when the event is raised, each method in the list of delegates is raised, in the same order as they were registered in.

The following line:

1 container.MyEvent += new PlayingWithEventHandlers( Console.Out.WriteLine ).Handler;

Expands out to something like this…

1 PlayingWithEventHandlers handlers = new PlayingWithEventHandlers( Console.Out.WriteLine );
2   MulticastDelegate multicastDelegate = new MulticastDelegate( handlers, "Handler" );

You’ll find that the MulticastDelegate constructor is protected so this code wont actually compile. Also, the event delegate is combining the newly added delegate that is registering for the event.

The following example uses 3 different delegates defined in the .NET Framework.

 1 //public delegate void EventHandler(object sender, EventArgs e);
 2   //public delegate void TimerCallback(object state);
 3   //public delegate void Action<T>(T obj);
 4 
 5   public class PlayingWithEventHandlers 
 6   {
 7       private readonly Action< string > _action;
 8 
 9       public PlayingWithEventHandlers( Action< string > action ) 
10       {
11           _action = action;
12       }
13 
14       public void Handler( object sender, EventArgs e ) 
15       {
16           _action( ( string )sender );
17       }
18   }
19 
20   public class MyEventContainer {
21       private Timer _timer;
22 
23       public MyEventContainer( ) 
24       {
25           _timer = new Timer( delegate( object state ) { 
26             MyEvent.Invoke( state, EventArgs.Empty ); 
27           }, "Tick", 0, 5000 );
28       }
29 
30       public event EventHandler MyEvent;
31   }
32 
33   public class Bootstrap 
34   {
35       public static void Main( ) 
36       {
37           MyEventContainer container = new MyEventContainer( );
38           container.MyEvent += new PlayingWithEventHandlers( Console.Out.WriteLine ).Handler;
39           Console.In.ReadLine( );
40       }
41   }

One of the benefits of using delegates that you can inline an anonymous delegate. The above line where the an instance of the Timer class is constructed could be re-written like…

1 _timer = new Timer( delegate { MyEvent.Invoke( "Tick", EventArgs.Empty ); }, null, 0, 5000 );

The TimerCallBack’s input parameter “state” is still passed to the delegate but the C# compiler allows for this shorthand which ignores the input parameter. The same code could also be re-written as…

1 public MyEventContainer( ) 
2   {
3       _timer = new Timer( TimerClickHandler, "Tick", 0, 5000 );
4   }
5 
6   public void TimerClickHandler( object state ) 
7   {
8       MyEvent.Invoke( state, EventArgs.Empty );
9   }

This version creates a new method called “TimerClickHandler” that matches the “TimerCallback” delegate signature. Delegates can be quite fun… almost as fun as tables of function pointers in C.

For more information check out…

CLR via C#, Second Edition (Pro Developer) by Jeffrey Richter Read more about this title…

EventHandler.txt (1.22 KB)

comments powered by Disqus