Home >

Some tips on events

19. November 2008
public class PluginService:IPluginService
{
    public event EventHandler<EventArgs> PluginStarted;
    public event EventHandler<EventArgs> PluginStarting;
    public void StartPlugin(int id)
    {
        PluginStarting(this, EventArgs.Empty);
        //Do some work
        PluginStarted(this, EventArgs.Empty);
    }
}

There is something obviously wrong with this code.

First of all, it is not doing the null check on the event. One should not assume that event will always have handlers.

There are 2 solutions to that issue:

  1. Dummy operation  
    public class PluginService:IPluginService
    {
        public event EventHandler<EventArgs> PluginStarted = delegate { };
        public event EventHandler<EventArgs> PluginStarting = delegate { };
        public void StartPlugin(int id)
        {
            PluginStarting(this, EventArgs.Empty);
            //Do some work
            PluginStarted(this, EventArgs.Empty);
        }
    }


    or some people prefer null object pattern but it is restricted to some specific delegate type.

    public class PluginService:IPluginService
    {
        public event EventHandler<EventArgs> PluginStarted = NullSubscriber.For<EventArgs>();
        public event EventHandler<EventArgs> PluginStarting = NullSubscriber.For<EventArgs>();
        public void StartPlugin(int id)
        {
            PluginStarting(this, EventArgs.Empty);
            //Do some work
            PluginStarted(this, EventArgs.Empty);
        }
    }

     

  2. Null Check
    First try would be to do the null check with a simple if:
    public class PluginService:IPluginService
    {
        public event EventHandler<EventArgs> PluginStarted;
        public event EventHandler<EventArgs> PluginStarting;
        public void StartPlugin(int id)
        {
            if (PluginStarting != null)
                PluginStarting(this, EventArgs.Empty);
            //Do some work
            if (PluginStarted!=null)
                PluginStarted(this, EventArgs.Empty);
        }
    }



    This wouldn’t work, since during the execution after the if is evaluated, the event handler may unsubscribe from the event, which will result in a null reference exception in the runtime(which is hard to catch in development)

    Another and the correct one try is:

    public class PluginService:IPluginService
    {
        public event EventHandler<EventArgs> PluginStarted;
        public event EventHandler<EventArgs> PluginStarting;
        public void StartPlugin(int id)
        {
            var handler = PluginStarting;
            if (handler != null)
                handler(this, EventArgs.Empty);
            //Do some work
            handler = PluginStarted;
            if (handler != null)
                handler(this, EventArgs.Empty);
        }
    }


    The key point here is not the null check, but how we do the null check. Since the events are immutable, we are now free to assign it. It won’t cause an issue when one of the subscribers unsubscribe.
    (thanks to Ayende for reminding us the Race condition)

    I think this is enough for the first post

    kick it on DotNetKicks.com

, ,

Comments

11/21/2008 11:55:12 AM #
Trackback from DotNetKicks.com

Some tips on events
12/19/2008 8:59:17 AM #
AFAIK you should always be able to use the anonymous method what you call the dummy operation.
Once it's been assigned as a listener to the event it's no longer possible to unsuscribe with another anonymous method.
And my understanding this means you no longer need to do the null check because you're guaranteed there will always be at least one subscriber to the event.
Now regarding the immutability of the event, the null check is here (well no longer if one uses the anonymous method) as a safeguard measure if there isn't any subscriber but you still run in a possible situation where you copy the event handlers to another event object and before you actually call these handlers one of them may have unsubscribed.
This is still a race condition that is likely not to be of great concern but by having a null check we make sure we can't have null reference exceptions.
12/19/2008 9:20:58 AM #
Hi Daniel,

you should always be able to use the anonymous method what you call the dummy operation.

Once it's been assigned as a listener to the event it's no longer possible to unsuscribe with another anonymous method.


Exactly, this is what my code shows.

And my understanding this means you no longer need to do the null check because you're guaranteed there will always be at least one subscriber to the event.

Correct

Now regarding the immutability of the event, the null check is here (well no longer if one uses the anonymous method) as a safeguard measure if there isn't any subscriber but you still run in a possible situation where you copy the event handlers to another event object and before you actually call these handlers one of them may have unsubscribed.

Correct.

This is still a race condition that is likely not to be of great concern but by having a null check we make sure we can't have null reference exceptions.

Correct.
5/19/2009 5:13:30 AM #
Trackback from Tuna Toksoz

Id Generation for db4o
7/23/2010 10:23:46 PM #
Id Generation for db4o

Id Generation for db4o
3/23/2011 3:32:09 AM #
Thank you for making the effort and showing this information with us all. It was certainly very helpful and informative while being straight forward and to the point.
Comments are closed