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.

 

Oren Ellenbogen

 

6 thoughts on “Safe events with .Net

  1. Hi oren,
    i tried to think how can your third example (the third code sample) can throw some exception, it seems to me that in the first line you take all event listeners to your "hands" so no one can changge it, furthermore if it’s private member (handler)

    also,about the last example- can you give some example of how you can have listener in your event that cause you to throw exception?

    Dror

  2. Hi,
    How this line can prevent the bug of removing one of the listeners?!
    EventHandler handler = this.Click;
    it seems to me like reference to EventHandler isn’t it?
    thanks.

  3. @Omer – didn’t notice you (I’m not so sure how do you look though).

    @Human-Debuggger –
    The case here is thread-safety and not handling an exception raised by one of the listeners. By assinging the current event to inner variable, the check for not null will be thread safe now.

  4. @Dror – we talked about it in the office but I’ll answer here as well, maybe it could help someone else. The case here is to trigger our entire invocation list even if one of the listeners throws an exception. Here is an exmple that one of the listeners throws an exception:

    public class A
    {
    public event EventHandler Click = delegate() {};

    public void DoAction()
    {
    this.Click(this, EventArgs.Empty);
    }
    }

    Main()
    {
    A a = new A();
    a.Click += delegate(object sender, EventArgs e)
    {
    Console.WriteLine("Hey");
    }

    a.Click += delegate(object sender, EventArgs e)
    {
    // .. some logic here …
    throw new Exception("Bad logic");
    }

    a.Click += delegate(object sender, EventArgs e)
    {
    Console.WriteLine("Bye");
    }

    a.DoAction();
    }

    The exception in one of our listeners prevent the other listeners to be triggers. In infrastructure classes this isn’t acceptable. Think about a Broker class that recieve a message and responsible to look for stock buyers\sellers. If one of the buyers\sellers is sick or just unresponsive, the Broker should carry on and look for others.

    The solution is to wrap each call to the listener with try-catch block as I’ve demonstrated.

Comments are closed.