I\'m learning about Events / Delegates in C#. Could I ask your opinion on the naming/coding style I\'ve chosen (taken from the Head First C# book)?
Am teaching a fr
I would say the best guide to events in general, including naming conventions, is here.
It is the convention I have adopted, briefly:
In your case it could be:
class Metronome {
event Action Ticked;
internalMethod() {
// bla bla
Ticked();
}
}
Above sampple use below convention, self-describing ;]
Events source:
class Door {
// case1: property change, pattern: xxxChanged
public event Action<bool> LockStateChanged;
// case2: pure action, pattern: "past verb"
public event Action<bool> Opened;
internalMethodGeneratingEvents() {
// bla bla ...
Opened(true);
LockStateChanged(false);
}
}
BTW. keyword event
is optional but enables distinguishing 'events' from 'callbacks'
Events listener:
class AlarmManager {
// pattern: NotifyXxx
public NotifyLockStateChanged(bool state) {
// ...
}
// pattern: [as above]
public NotifyOpened(bool opened) {
// OR
public NotifyDoorOpened(bool opened) {
// ...
}
}
And binding [code looks human friendly]
door.LockStateChanged += alarmManager.NotifyLockStateChanged;
door.Moved += alarmManager.NotifyDoorOpened;
Even manually sending events is "human readable".
alarmManager.NotifyDoorOpened(true);
Sometimes more expressive can be "verb + ing"
dataGenerator.DataWaiting += dataGenerator.NotifyDataWaiting;
Whichever convention you choose, be consistent with it.
A point I have found after using events in .Net for many years is the repetitive need to check the event for a null handler on every invocation. I'm yet to see a piece of live code that does anything but not call the event if it is null.
What I have started doing is to put a dummy handler on every event I create to save the need to do the null check.
public class Metronome
{
public event EventHandler Tick =+ (s,e) => {};
protected virtual void OnTick(EventArgs e)
{
Tick(this, e); // now it's safe to call without the null check.
}
}
There are a few points that I would mention:
Metronome.OnTick doesn't seem to be named correctly. Semantically, "OnTick" tells me it will be called when it "Tick"s, but that isn't really what's happening. I would call it "Go" instead.
The typically accepted model, however would be to do the following. OnTick
is a virtual method that raises the event. This way, you can override the default behavior in inherited classes easily, and call the base to raise the event.
class Metronome
{
public event EventHandler Tick;
protected virtual void OnTick(EventArgs e)
{
//Raise the Tick event (see below for an explanation of this)
var tickEvent = Tick;
if(tickEvent != null)
tickEvent(this, e);
}
public void Go()
{
while(true)
{
Thread.Sleep(2000);
OnTick(EventArgs.Empty); //Raises the Tick event
}
}
}
Also, I know this is a simple example, but if there are no listeners attached, your code will throw on Tick(this, EventArgs.Empty)
. You should at least include a null guard to check for listeners:
if(Tick != null)
Tick(this, EventArgs.Empty);
However, this is still vulnerable in a multithreaded environment if the listener is unregistered between the guard and the invocation. The best would be to capture the current listeners first and call them:
var tickEvent = Tick;
if(tickEvent != null)
tickEvent(this, EventArgs.Empty);
I know this is an old answer, but since it's still gathering upvotes, here's the C# 6 way of doing things. The whole "guard" concept can be replaced with a conditional method call and the compiler does indeed do the Right Thing(TM) in regards to capturing the listeners:
Tick?.Invoke(this, EventArgs.Empty);
Looks good, aside from the fact that OnTick
doesn't follow the typical event invocation model. Typically, On[EventName]
raises the event a single time, like
protected virtual void OnTick(EventArgs e)
{
if(Tick != null) Tick(this, e);
}
Consider creating this method, and renaming your existing "OnTick
" method to "StartTick
", and instead of invoking Tick
directly from StartTick
, call OnTick(EventArgs.Empty)
from the StartTick
method.
Interesting how Microsoft seems to break its own naming conventions with Visual Studio generated event handler names.
See: Event Naming Guidelines (.NET Framework 1.1)