Introduction
Some time ago I explained to one of my friends the benefits of using GetInvocationList when raising events over simply calling the event handler delegate directly. He understood my explanation and they implemented their events using the pattern.
Well today he was asked by another engineer why the code is always using "this GetInvocationList call for events" and so I figured I could revisit the explanation a little more formally both for his benefit as well as the other 3 people that read this blog.
The Challenge of Multicast Delegates
An event is, for all intents and purposes, a muticast delegate. What that means is that you can have 0:n listeners listening for that event. When you get you call the delegate directly in your code, the CLR iterates through all of the listeners and calls them for you without you having to know how many listeners there are. This is all well and good *when things go right* but the problem is that things don't always go right. This is especially the case if you're writing a class that will be consumed by someone else and you have no control over what their handler might be doing.
What happens if a handler throws an exception? Well the CLR stops iterating through delegates an raises that exception to the method that invoked the delegate. All handlers that were in the delegate list after the exception *will never see the event*.
To be a little more concrete, let's look at an example.
Let's say we have a class that publishes an event. We'll call it Publisher and name the event the very original name "MyEvent".
class Publisher
{
public event EventHandler MyEvent;
public Publisher()
{
}
}
Now when we want to raise that event, the most common thing I've seen is to just get a handler and call it (ensuring it's not null). Something like this:
public void RaiseDirect()
{
var handler = MyEvent;
if (handler == null)
{
Console.WriteLine("No listeners");
return;
}
try
{
handler(this, null);
}
catch(Exception)
{
Console.WriteLine("A listener threw an exception in its handler");
}
}
Now let's look at where this falls down. First let's create a couple listeners. Good ones that successfully run an EventHandler and bad ones that throw an exception in their EventHandler:
class Listener
{
static int m_number = 0;
public Listener()
{
m_number++;
Name = this.GetType().Name + m_number.ToString();
}
public string Name { get; private set; }
}
class GoodListener : Listener
{
public GoodListener(Publisher publisher)
{
publisher.MyEvent += new EventHandler(publisher_MyEvent);
}
void publisher_MyEvent(object sender, EventArgs e)
{
Console.WriteLine(string.Format("{0} handled event", Name));
}
}
class BadListener : Listener
{
public BadListener(Publisher publisher)
{
publisher.MyEvent += new EventHandler(publisher_MyEvent);
}
void publisher_MyEvent(object sender, EventArgs e)
{
Console.WriteLine(string.Format("{0} threw in handler", Name));
throw new Exception("failure");
}
}
Now let's wire these up and see what happens with some quick test code:
List<Listener> m_listeners = new List<Listener>();
Publisher publisher = new Publisher();
for (int i = 0; i < 5; i++)
{
if (i % 3 == 0)
{
m_listeners.Add(new BadListener(publisher));
}
else
{
m_listeners.Add(new GoodListener(publisher));
}
}
Console.WriteLine("Direct event");
Console.WriteLine("------------");
publisher.RaiseDirect();
We run this and we get the following:

You'll see that only 1 handler (a bad one) ran. It threw an exception and no one else knew jack about the event.
Using the Invocation List
Now let's look at how we get past this. The EventHandler delegate exposes a method called GetInvocationList which returns an array of all of the delegates. Using this we can iterate across all of the EventHandlers manually. Something like this:
public void RaiseIterative()
{
var handler = MyEvent;
if (handler == null)
{
Console.WriteLine("No listeners");
return;
}
foreach (EventHandler h in handler.GetInvocationList())
{
try
{
h(this, null);
}
catch(Exception)
{
Console.WriteLine("A listener threw an exception in its handler");
}
}
}
Now when we run our test code, raising the events through the iterative handling routine, we get this

As you can see, *all* of the EventHandlers ran.
Conclusion
We can't always control the code that other developers write (I'd argue we can't even really control our own code). If you're writing code that's raising events to be consumed by any handlers (and most events I've seen are meant to be consumed) then you need to think defensively. How can you protect your consumers from one another's bad behavior? By coding defensively, that's how. Simple patterns like calling GetInvocationList is a good defensive strategy. It not only makes your own code more solid, it prevents those support calls from developers complaining that your assembly isn't raising events, even when they were the root cause.
Get the full source code here: InvocationTest.zip (27.37 KB)