Safe events with .Net
This is another brilliant idea from Juval Lowy. I got to see him speak about unify ASP.NET and WinForms security models last week and during the session (which was really great by the way, but that’s for another day), I’ve notice this genius row:
public event EventHandler<EventArgs> Click = delegate {};
Isn’t it a beauty ?!
I know, it doesn’t ring a bell yet, so I’ll make the picture a bit clearer. But before we move forward, let’s take a few steps backward.
Starting from scratch, a reminder:
We want to raise our Click event.
I’m sure you are familiar with these lines:
private void OnClick(EventArgs e)
{
if (this.Click != null) // check if the invocation list is empty
this.Click(this, e);
}
The reason we do it is we we don’t want to raise an event with an empty invocation list; This will raise an exception, which is a big no-no if we can avoid it. simple ah ?
This implementation is problematic as between the check (if statement) and the activation the listener can be removed. A quick fix will look like this:
private void OnClick(EventArgs e)
{
EventHandler<EventArgs> handler = this.Click;
if (handler != null) // check if the invocation list is empty
handler(this, e);
}
Ok, so it’s a little safer invocation now (I remember a version with locking, but the above will do for now).
Rewind:
Now let’s get back to Juval’s genius line I’ve showed before:
public event EventHandler<EventArgs> Click = delegate {};
attaching delegate{} to the invocation list will create (behind the scene) a class, with a random name, with an empty method (matching EventHandler<EventArgs> signature), with a random name.
Why is this so brilliant ? Because now we verified that the the event invocation list is
always filled with *at least* one (empty)listener.
We can’t clear the empty listener as we don’t have it’s instance (it was generated for us) nor it’s name.
The checks for not-empty invocation list can be thrown to the garbage. We now have a safe event, guaranteed.
This is certainly a best practice for working with events (following Framework Design Guidelines structure):
Do Consider attaching an empty delegate to your event.
Recap
public class MyClass
{
public event EventHandler Click = delegate {};
private void OnClick(EventArgs e)
{
// no need to check if (this.Click!=null)
this.Click(this, e);
}
}
nice and easy.
Extra: one more step for safe events:
If we’re talking about safe events, there is an advanced scenario in which you want the “eat” exception raised by one of the listeners, so the the other listeners will still be triggered. In the above, any exception in one of our listeners will stop the triggering of our invocation list.
The solution is quite simple also:
private void OnClick(EventArgs e)
{
EventHandler<EventArgs> handler = this.Click;
if(handler != null)
{
Delegate[] list = handler.GetInvocationList();
foreach (Delegate del in list)
{
try
{
EventHandler<EventArgs> listener = (EventHandler<EventArgs>)del;
listener(this, e);
}
catch { }
}
}
}
Juval even wrote a cool generic utility class named EventsHelper which you can get here.
Brilliant.