The traditional pattern for writing events in C# has been a 3 step process:
- Define the event in your class.
- Define a utility function to check if the event isn’t null, then fire it.
- Write code that calls the utility function.
An example:
public event Action<object,EventArgs> MyEvent;
protected virtual void OnAction(object sender, EventArgs args)
{
if(MyEvent != null)
MyEvent(sender, args);
}
...
//somewhere late in code
OnAction(this, new EventArgs);
This pattern, or variants of it, can be found all over the .Net framework, in various component suites, and in many .Net applications. I have used it for years. But with the advent of .Net 3.5′s abbreviated “lambda” style for delegate construction, a new pattern has emerged. It took a while for me to convince myself that it is better, but I now use it all of the time. This is also a three step process:
- Define the event in your class.
- Write an anonymous delegate for the event in the class constructor.
- Write code that directly fires the event.
An example:
public event Action<object,EventArgs> MyEvent;
public MyClass()
{
//an anonymous delegate that does nothing
MyEvent += (sender, e) => {};
}
...
//somewhere late in code
OnAction(this, new EventArgs);
Of course this could also be done in .Net 2.0 using the old anonymous delegate syntax, and since it all runs on the same CLR, its prolly the exact same IL.
The vital difference in these two patterns is the checking for a null value of the event. In the first pattern, a conditional statement is checking to make sure that the event is not null i.e. something is subscribed to the event. The second pattern doesn’t have to check this because we subscribed to the event in the constructor. The event will never be null. What are the pros of doing it the second way?
- No conditional code needs to be written. This eliminates bugs.
- Repetitive code is eliminated.
- The code is easier to follow. Events are fired directly instead of going through a utility function.
- The code is easier to maintain, since there is less code.
- This is a rather anal point, but this code is also more thread safe. In the first pattern an event could theoretically have a subscriber when checked, and not have one by the time it is called. Like I said, pretty anal but true.
Of course the con here is creating a delegate for an event that may never be fired. Delegates take up some small amount of ram, yada yada yada. The OO prgrammer in me didn’t like the smell of this solution. The pragmatist in me won out. It is easily worth the trade-off in the small amount of resources needed for the anonymous delegates in the 2nd pattern to eliminate all of the cons of the first pattern.
