1

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);
    }
}
Vlad
  • 479
  • 4
  • 5
  • 3
    I think this would be more appropriate on http://codereview.stackexchange.com/ – Jamie Dixon Apr 09 '12 at 08:24
  • You can simply use `var temp = handler`. Assignment of a ref type is atomic. – H H Apr 09 '12 at 08:36
  • With accordance to Jeffrey Richter in his book CLR via C# CompareExchange is used to provide a kind of safety way to deal with events in multi-threaded environment. CompareExchange gives you some confidence that compiler won't optimize the assignment (although which is very unlikely). Agree, that it's sort of overhead, however better to be protected against to any changes in compiler behaviour – Vlad Apr 09 '12 at 09:37

0 Answers0