Technorati Tags:
.NET,
Tests Verified some interesting behavior today that I had suspected but wasn’t entirely sure of. It appears that a System.Delegate’s identity is determined by the object instance (or type) and method that it points to, and that the Combine and Remove methods (called when you add or remove an event handler) perform some kind of reference counting to determine how many times equivalent delegates should be invoked.
A simple example of this behavior can be demonstrated by creating two EventHandler delegates pointing to the same handler method. Add one of those delegates to the event, but remove the other…the handler will never be called when the event is raised since both delegates are equivalent. However, if you add the same delegate multiple times to the event, then the handler will be called as many times as the delegate was added, and you must remove the delegate (or an equivalent delegate) at least the same number of times in order for the handler not to be called.
Here’s the Console app used to verify this behavior.
using System;
using System.ComponentModel;
internal class Program
{
private static readonly object myEventKey = new object();
private readonly EventHandlerList eventHandlers = new EventHandlerList();
private event EventHandler MyEvent
{
add
{
eventHandlers.AddHandler(myEventKey, value);
}
remove
{
eventHandlers.RemoveHandler(myEventKey, value);
}
}
private static void Main(string[] args)
{
Program program = new Program();
Console.WriteLine("Adding two delegates with distinct identities pointing to the same method.");
EventHandler myEventHandler1 = MyEventHandler;
EventHandler myEventHandler2 = MyEventHandler;
program.MyEvent += myEventHandler1;
program.MyEvent += myEventHandler2;
Console.WriteLine("Raising MyEvent (expecting MyEvent to be called twice).");
program.OnMyEvent(EventArgs.Empty);
Console.WriteLine("Removing a new delegate pointing to the previously added methods.");
EventHandler myEventHandler3 = MyEventHandler;
program.MyEvent -= myEventHandler3;
Console.WriteLine("Raising MyEvent (expecting MyEvent to be called twice, once for myEventHandler1 and once for myEventHandler2).");
program.OnMyEvent(EventArgs.Empty);
Console.WriteLine("Removing myEventHandler1 from the event handler list.");
program.MyEvent -= myEventHandler1;
Console.WriteLine("Raising MyEvent (expecting MyEvent to be called once, for myEventHandler2.");
program.OnMyEvent(EventArgs.Empty);
Console.WriteLine("Creating a specific delegate that will be used in both add and remove.");
EventHandler myEventHandler = MySpecificEventHandler;
Console.WriteLine("Adding specific delegate to the handler list.");
program.MyEvent += myEventHandler;
Console.WriteLine("Raising MyEvent, (expecting MyEvent to be called twice, once for myEventHandler2 and once for myEventHandler.");
program.OnMyEvent(EventArgs.Empty);
Console.WriteLine("Removing specific delegate from the handler list.");
program.MyEvent -= myEventHandler;
Console.WriteLine("Raising MyEvent, (expecting MyEvent to be called once, for myEventHandler2 that has never been removed.");
program.OnMyEvent(EventArgs.Empty);
Console.WriteLine("Press the 'Enter' key to stop the server.");
Console.ReadLine();
}
private static void MyEventHandler(object sender, EventArgs e)
{
Console.WriteLine("MyEventHandler Called.");
}
private static void MySpecificEventHandler(object sender, EventArgs e)
{
Console.WriteLine("MySpecificEventHandler Called.");
}
private void OnMyEvent(EventArgs e)
{
EventHandler myEvent = (EventHandler)eventHandlers[myEventKey];
if (myEvent != null)
{
myEvent(this, e);
}
}
}
…and this is the output of the test above:
Cool. I became sure of something today that previously I had only thought I knew.