EventHandler Extension Method
I just wrote an interesting extension method, and thought I’d share it. If you’re using the EventHandler<T> delegate that was introduced back in .NET framework 2.0, this method might come in handy:
public static class ExtensionsForEventHandler
{
public static void Raise<T>(this EventHandler<T> handler, object sender, T args)
where T : EventArgs
{
EventHandler<T> evt = handler;
if (evt != null) evt(sender, args);
}
}
This helps you avoid having to repeat the boilerplate code that goes along with the typical event-firing pattern. With this extension method, you can do this instead:
public class StuffDoer
{
public event EventHandler<StuffEventArgs> StuffHappened;
public void DoStuff()
{
StuffHappened.Raise(this, new StuffEventArgs());
}
}
Since extension methods can be called on references that are actually null, this will work even if no listeners have attached to the StuffHappened method.
I’m all about syntactic sugar, and extension methods provide an easy way to improve the readability of your code without too much hassle. (And you are writing your code so it’s easier for others to read, right?
No need for the extra evt variable, by the way, as the handler argument has a nice safe copy of the original handler anyway. As such, you can just do a null check on handler and then fire away!
That’s a very nice use of extension methods, Nate! I will definitely use it!
The evt variable should be somewhat useful for race condition handling.
re: “The evt variable should be somewhat useful for race condition handling.”
No. As mentioned in my original comment the evt variable is unnecessary, even for “race condition handling”, as a point-in-time reference to the handler has already been correctly captured via the argument to the method.
Duh! You’re of course perfectly correct! I didn’t see the context for all the goodies..:)
Duh=Doh
Nate – Great use of extension methods! I really like the readability of this. I’m trying to think of a way to do something similar to support INotifyProperty Change in Silverlight… should be interesting!
In “Accelerated C# 2008″ it’s author shows a bit of code
public class PlayerUI {
public event EventHandler PlayEvent;
protected virtual void OnPlay() {
EventHandler localHandler = PlayEvent;
if( localHandler != null ) {
localHandler( this, new PlayEventArgs(“somefile.wav”) );
}
}
}
and then said, “In C#, you must test the event for null before calling it; otherwise, the result could be a NullReferenceException. The OnPlay method
makes a local copy of the event before testing it for null. This avoids the race condition where the event is set to null from another thread after the null check passes and before the event is raised.”
Could you please explain (or provide a link to explanations of) how exactly could event be set to null from another thread? Since making another localHandler reference to the same object in a code above (and copying an original reference to a handler variable internal to Raise() method in your code) helps to prevent race condition, I assume that another thread could re-assign initial global event reference to null, while a local copy of event reference stays good. But how exactly and why another thread does it?
Thank you!
I created something very similar to this in our codebase. I used to put a RaiseEvent method on each INotifyPropertyChanged implementor, but now I’ve got a set of handy extension methods for event raising.
If your events are going to be fired very frequently this could affect performance. With your extension you create an instance of EventArg every time, whereas in the normal method of using an if(event == null) the EventArgs won’t be instantiated if no one is interested in the event.
I’d be tempted to use it for readability sake, though needs documenting otherwise the next person to see it would probably put if (event == null) in front of it
Ben, could you elaborate? Why did you say an instance of EventArgs is created every time? Isn’t it created only when Raise() method is actually called?
It seems I’m unable to raise an event defined on a base class.