0

I am developing an application which is event driven and runs mulit-threaded and so I have a lot of events getting fired and to fire them "in a save way" I do this the following way:

public static void OnEVENT(EventArgs args)
{
    var theEvent = EVENT;
    if (theEvent != null)
        theEvent(this, args);
}

This pattern is repeated multiple times all over my application.

Is there a way to get rid of the repetion of this pattern and implent this in a generic way?

Christoph Fink
  • 22,727
  • 9
  • 68
  • 113
  • I would personally just get rid of `null` as a valid event and introduce a default event that just does nothing when called. – Voo Apr 23 '14 at 14:34
  • I have events fired 100.000 times per second and the "null-delegate solution" is slower then my solution below... – Christoph Fink Apr 23 '14 at 14:35
  • By any appreciable amount after the JIT has run? I'd like to see a benchmark for that one. – Voo Apr 23 '14 at 14:36
  • I don't have the benchmarks around anymore, but I did them some time ago... I just posted this now as I was asked for it in another question. – Christoph Fink Apr 23 '14 at 14:42
  • Not a "real test", but execute the following in LINQPad: http://pastebin.com/K1gXu4AJ => the extension is marginally faster. BUT if you would do it "directly" it would be more then twice as fast: http://pastebin.com/wDXCeLnL – Christoph Fink Apr 23 '14 at 14:52
  • As I said: *after* the JIT has run I doubt there's an appreciable difference. Your test clearly only tests interpreted code. Although writing a correct benchmark for this that doesn't lead to the if branch being eliminated in the loop wouldn't be as simple either. I may get to it tonight.. – Voo Apr 23 '14 at 14:55
  • Ok, same in a console app (release build): 100M iterations: Extension syntax => ~290ms; Method call => ~256ms; null-delegate => ~340ms (http://postimg.org/image/6g5ze9dml/) – Christoph Fink Apr 23 '14 at 15:07
  • The problem is *how* you benchmark the code not where you run it. As far as I can see there isn't any good thread on SO about this, but [here's](http://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java) a helpful one about Java which also has lots of language neutral information. The post by Jon also links to C# specific posts. In this particular case taking care that the if statement isn't hoisted out of the loop should also take extra care. – Voo Apr 23 '14 at 15:30
  • But at this differences (or total times at all) I must admit the performance difference are negligible and so I prefer the extensions as they are more comfortable (some of them do the EventArgs construction for you, no initialization needed)... – Christoph Fink Apr 23 '14 at 15:44

1 Answers1

0

I created a Helper class containing multiple extensions, so I do not need the OnEVENT methods at all and can simply call EVENT.Raise(this, args);:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;

namespace System
{
    /// <summary>Collection of common extensions.</summary>
    public static class EventExtensions
    {
        /// <summary>Raises the specified event.</summary>
        /// <param name="theEvent">The event.</param>
        /// <param name="sender">The sender.</param>
        /// <param name="args">The <see cref="EventArgs"/> instance containing the event data.</param>
        public static void Raise(this EventHandler theEvent, object sender, EventArgs args = null)
        {
            if (theEvent != null)
                theEvent(sender, args);
        }
        /// <summary>Raises the specified event.</summary>
        /// <typeparam name="T">The type of the event argument.</typeparam>
        /// <param name="theEvent">The event.</param>
        /// <param name="sender">The sender.</param>
        /// <param name="args">The arguments.</param>
        public static void Raise<T>(this EventHandler<T> theEvent, object sender, T args) where T : EventArgs
        {
            if (theEvent != null)
                theEvent(sender, args);
        }
        /// <summary>Raises the specified event.</summary>
        /// <typeparam name="T">The value type contained in the EventArgs.</typeparam>
        /// <param name="theEvent">The event.</param>
        /// <param name="sender">The sender.</param>
        /// <param name="args">The arguments.</param>
        public static void Raise<T>(this EventHandler<EventArgs<T>> theEvent, object sender, T args)
        {
            if (theEvent != null)
                theEvent(sender, new EventArgs<T>(args));
        }
        /// <summary>Raises the specified event.</summary>
        /// <typeparam name="T">The value type contained in the EventArgs.</typeparam>
        /// <param name="theEvent">The event.</param>
        /// <param name="sender">The sender.</param>
        /// <param name="newValue">The new value.</param>
        /// <param name="oldValue">The old value.</param>
        public static void Raise<T>(this EventHandler<ValueChangedEventArgs<T>> theEvent, object sender, T newValue, T oldValue)
        {
            if (theEvent != null)
                theEvent(sender, new ValueChangedEventArgs<T>(newValue, oldValue));
        }

        /// <summary>Raises the specified event for every handler on its own thread.</summary>
        /// <typeparam name="T">The value type contained in the EventArgs.</typeparam>
        /// <param name="theEvent">The event.</param>
        /// <param name="sender">The sender.</param>
        /// <param name="args">The arguments.</param>
        public static void RaiseAsync<T>(this EventHandler<EventArgs<T>> theEvent, object sender, T args)
        {
            if (theEvent != null)
            {
                var eventArgs = new EventArgs<T>(args);
                foreach (EventHandler<EventArgs<T>> action in theEvent.GetInvocationList())
                    action.BeginInvoke(sender, eventArgs, null, null);
            }
        }
    }
}

and the ValueChangedEventArgs class used in there:

/// <summary>EventArgs for notifying about changed values.</summary>
/// <typeparam name="T">The type of the contained value.</typeparam>
public class ValueChangedEventArgs<T> : EventArgs
{
    /// <summary>Initializes a new instance of the <see cref="ValueChangedEventArgs{T}" /> class.</summary>
    /// <param name="newValue">The new value.</param>
    /// <param name="oldValue">The old value.</param>
    public ValueChangedEventArgs(T newValue, T oldValue)
    {
        NewValue = newValue;
        OldValue = oldValue;
    }

    /// <summary>Gets the new value.</summary>
    /// <value>The new value.</value>
    public T NewValue { get; private set; }
    /// <summary>Gets the old value.</summary>
    /// <value>The old value.</value>
    public T OldValue { get; private set; }
}
Christoph Fink
  • 22,727
  • 9
  • 68
  • 113