Firing multicast delegate events

I am currently writing an ASP.NET server control, which exposes a
number of custom events.  I was looking through my code archives
for a helper class, which will safely call handler methods of a
multicast delegate, when an event is triggered.  I found the class
I was looking for, and decided to publish it here on my blog for future
reference.

A quick note on multicast delegates – Multicast delegates are delegate
instances that are derived from System.MulticastDelegate
and can have
more than one instance in their invocation list – essentially they are
delegates
that allow assignment of multiple handler methods. When dealing with
multicast delegates, a question arises about how to deal with
exceptions thrown in handler methods. When the framework executes a
multicast delegate, all handler methods in the invocation list are
executed sequentially.  If one of the invoked handler methods
should throw an exception, the remaining handlers in the
list, yet to be executed, are not invoked.

So, let’s say that we have a custom event on a control (events are
multicast delegates). The containing code of the control subscribes to
the control’s event with two different event handlers.  The event
fires, causing the first handler to be invoked, which then happens to
throw an exception.  The second event handler will never get
called, which could cause serious malfunction or resource leakage in
the application if the handler had an important part to play. The
helper class, posted below, alleviates this problem by invoking the
handler methods in a delegate invocation list dynamically, whilst
trapping exceptions.

The helper class exposes methods to execute a multicast delegate, both
synchronously and asynchronously.  The synchronous method returns
the list of thrown exceptions, whilst calling all handlers in the
invocation list.  The asynchronous method ignores any exceptions
and calls each handler in the invocation list on a separate thread.

public class EventHelper
{
///
/// Asynchronous delegate.
///
private delegate void AsyncFire(Delegate del, object[] args);

///
/// Static instance of asynchronous delegate.
///
private static AsyncFire _asyncFire = new AsyncFire(InvokeDelegate);

///
/// Call back for asynchronous delegate call.
///
private static AsyncCallback _asyncCallback = new AsyncCallback(InvokeDone);

///
/// Fire synchronously.
///
/// If an event handler throws an exception ignore it and move onto the next handler.
/// Multicast delegate.
/// Args.
public static Exception[] Fire(Delegate del, params object[] args)
{
List exceptions = new List();
// If delegate has no handlers then do nothing.
if (null != del)
{
// Iterate the handlers.
Delegate[] delegates = del.GetInvocationList();
foreach (Delegate sink in delegates)
{
try
{
// Invoke.
sink.DynamicInvoke(args);
}
catch (Exception ex)
{
exceptions.Add(ex);
}
}
}
return exceptions.ToArray();
}

///
/// Fire asynchronously.
///
/// Each handler is fired in an thread from the thread pool.
/// Multicast delegate.
/// Args.
public static void FireAsync(Delegate del, params object[] args)
{
// If delegate has no handlers then do nothing.
if (null != del)
{
// Iterate the handlers.
// Cannot call multicast delegate asyncronously, must process
// each handler separate.
Delegate[] delegates = del.GetInvocationList();
foreach (Delegate sink in delegates)
{
// Invoke using a thread from the pool.
_asyncFire.BeginInvoke(sink, args, _asyncCallback, null);
}
}
}

///
/// Invoke container delegate.
///
/// Delegate.
/// Args.
static void InvokeDelegate(Delegate del, object[] args)
{
del.DynamicInvoke(args);
}

///
/// Called when asynchronous delegate call complete.
///
/// Asynchronous result.
private static void InvokeDone(IAsyncResult ar)
{
// Clean up.
_asyncFire.EndInvoke(ar);
}
}