Why you should use EventHandler.GetInvocationList

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:


directevent.png


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


iteratedevent.png


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)

4 thoughts on “Why you should use EventHandler.GetInvocationList”

  1. Very good article on InvocationList. I don’t know about this exception handling before and I was under assumption that CLR will take care of any exception thrown by the handler. Thanks for this post.

    Murali.

    Like

  2. I disagree. By stopping to call other handlers the CLR is doing the only correct thing. The alternatives are simply too horrible:
    – Swallow exceptions: big no go, you really don’t want to ignore OutOfMemory, ThreadInterupt and others
    – Store an exception and rethrow it when all handlers have been called: What do you do when more than one handler throws? You can’t rethrow two exceptions at once, so you end up swallowing one of them.

    The same reasoning applies to your GetInvocationList solution. In the end, all you’re doing is ignoring exceptions that are potentially problematic to ignore.

    Regards,

    Andreas

    Like

    1. “By stopping to call other handlers the CLR is doing the only correct thing.”

      That’s a confused response, because the author didn’t say that the CLR isn’t doing the correct thing. Being able to invoke all the handlers with one call is a convenience, and if you’re fine with the convenience and use it, then yes, the CLR does the right thing, but this approach provides functionality that the convenience does not.

      “you really don’t want to ignore OutOfMemory, ThreadInterupt and others”

      But this applies ANY time you catch an exception … you should rethrow OutOfMemory or call Environment.FailFast. (However, .NET idiotically throws OutOfMemory for a StringBuilder that exceeds its capacity, which is a totally different sort of exception.)

      There’s no problem with swallowing ThreadInterruptException. ThreadAbortedException is a different matter, but the system automatically rethrows it, so there’s no problem with swallowing it, because it doesn’t actually get swallowed.

      “In the end, all you’re doing is ignoring exceptions that are potentially problematic to ignore.”

      No, they are being reported on the console. You could, of course, deal with them some other way. But you seem to be under the impression that you should never catch an unexpected Exception without rethrowing it, which is not the case.

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s