I am trying to summarize all approaches to raise an event by introducing utility class. Would appreciate any feedback on the below implementation.
The idea of the developed helpers is to provide different ways of raising events, including exception handling and cross-thread marshaling if needed.
public static class EventUtils
{
// startdard practice to raise an event
// for more information, pls see: http://blogs.msdn.com/ericlippert/archive/2009/04/29/events-and-races.aspx
public static void Raise<TEventArgs>(EventHandler<TEventArgs> handler, object sender, TEventArgs args)
where TEventArgs : EventArgs
{
var temp = Interlocked.CompareExchange(ref handler, null, null);
if (temp != null)
{
temp(sender, args);
}
}
public static void Raise(EventHandler handler, object sender, EventArgs args)
{
var temp = Interlocked.CompareExchange(ref handler, null, null);
if (temp != null)
{
temp(sender, args);
}
}
public static void Raise(EventHandler handler, object sender)
{
Raise(handler, sender, EventArgs.Empty);
}
/// <summary>
/// Method helps to make thread-safe raise of event as well as exception-safe
/// The latest means that all exceptions occurred during the invocation will be collected
/// and thrown as an AggregateException at the end of the call
/// </summary>
/// <typeparam name="TEventArgs"></typeparam>
/// <param name="handler"></param>
/// <param name="sender"></param>
/// <param name="args"></param>
public static void RaiseSafely<TEventArgs>(EventHandler<TEventArgs> handler, object sender, TEventArgs args)
where TEventArgs : EventArgs
{
var temp = Interlocked.CompareExchange(ref handler, null, null);
if (temp != null)
{
List<Exception> exceptions = null;
foreach (EventHandler<TEventArgs> @delegate in temp.GetInvocationList())
{
var instanceMethod = @delegate.Target as ISynchronizeInvoke;
try
{
if (instanceMethod != null && instanceMethod.InvokeRequired)
{
// An exception might be thrown if the thread that should process the call is no longer active.
instanceMethod.EndInvoke(instanceMethod.BeginInvoke(@delegate, new[] { sender, args }));
}
else
{
@delegate.Invoke(sender, args);
}
}
catch (Exception ex)
{
if (exceptions == null)
exceptions = new List<Exception>();
exceptions.Add(ex);
}
}
if (exceptions != null)
{
throw new AggregateException(exceptions);
}
}
}
/// <summary>
/// Method helps to make thread-safe raise of event as well as exception-safe
/// The latest means that all exceptions occurred during the invocation will be collected
/// and thrown as an AggregateException at the end of the call
/// </summary>
/// <param name="handler"></param>
/// <param name="sender"></param>
/// <param name="args"></param>
public static void RaiseSafely(EventHandler handler, object sender, EventArgs args)
{
var temp = Interlocked.CompareExchange(ref handler, null, null);
if (temp != null)
{
List<Exception> exceptions = null;
foreach (EventHandler @delegate in temp.GetInvocationList())
{
var instanceMethod = @delegate.Target as ISynchronizeInvoke;
try
{
if (instanceMethod != null && instanceMethod.InvokeRequired)
{
instanceMethod.EndInvoke(instanceMethod.BeginInvoke(@delegate, new[] { sender, args }));
}
else
{
@delegate.Invoke(sender, args);
}
}
catch (Exception ex)
{
if (exceptions == null)
exceptions = new List<Exception>();
exceptions.Add(ex);
}
}
if (exceptions != null)
{
throw new AggregateException(exceptions);
}
}
}
/// <summary>
/// Method helps to make thread-safe raise of event as well as exception-safe
/// The latest means that all exceptions occurred during the invocation will be collected
/// and thrown as an AggregateException at the end of the call
/// </summary>
/// <param name="handler"></param>
/// <param name="sender"></param>
public static void RaiseSafely(EventHandler handler, object sender)
{
RaiseSafely(handler, sender, EventArgs.Empty);
}
}