ユーティリティ クラスを導入して、イベントを発生させるすべてのアプローチをまとめようとしています。以下の実装に関するフィードバックをいただければ幸いです。
開発されたヘルパーのアイデアは、必要に応じて例外処理やクロススレッド マーシャリングなど、イベントを発生させるさまざまな方法を提供することです。
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);
}
}