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:
- 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);
}
}
- 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
events, c#, multithreading