活动 – 命名约定和风格
我正在学习C#中的Events / Delegates。 请问您对我select的命名/编码风格(从Head First C#书中获得)的意见?
我明天正在教这个朋友,并试图用最优雅的方式来解释这个概念。 (认为理解一个主题的最好方法就是尝试教它!)
class Program { static void Main() { // setup the metronome and make sure the EventHandler delegate is ready Metronome metronome = new Metronome(); // wires up the metronome_Tick method to the EventHandler delegate Listener listener = new Listener(metronome); metronome.OnTick(); } }
public class Metronome { // a delegate // so every time Tick is called, the runtime calls another method // in this case Listener.metronome_Tick public event EventHandler Tick; public void OnTick() { while (true) { Thread.Sleep(2000); // because using EventHandler delegate, need to include the sending object and eventargs // although we are not using them Tick(this, EventArgs.Empty); } } }
public class Listener { public Listener(Metronome metronome) { metronome.Tick += new EventHandler(metronome_Tick); } private void metronome_Tick(object sender, EventArgs e) { Console.WriteLine("Heard it"); } }
NB代码重构从http://www.codeproject.com/KB/cs/simplesteventexample.aspx
有几点我会提到:
Metronome.OnTick似乎没有正确命名。 在语义上,“OnTick”告诉我,它会在“Tick”的时候被调用,但实际上并不是这样。 我会把它叫做“去”。
通常接受的模式,但是会做到以下几点。 OnTick
是引发事件的虚拟方法。 这样,您可以轻松地覆盖inheritance类中的默认行为,并调用基础来引发事件。
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 } } }
另外,我知道这是一个简单的例子,但是如果没有监听器,你的代码会抛出Tick(this, EventArgs.Empty)
。 你应该至less包括一个空警卫来检查听众:
if(Tick != null) Tick(this, EventArgs.Empty);
但是,如果监听器未在注册和调用之间注册,则这在multithreading环境中仍然很脆弱。 最好的办法是首先抓住当前的听众,然后打电话给他们:
var tickEvent = Tick; if(tickEvent != null) tickEvent(this, EventArgs.Empty);
我知道这是一个古老的答案,但由于它仍然收集起来,这是C#6的做事方式。 整个“守卫”概念可以用一个条件方法调用来替代,编译器在捕获监听器方面确实做了正确的事情:
Tick?.Invoke(this, EventArgs.Empty);
微软实际上编写了大量的命名指南,并将其放入MSDN库中。 您可以在这里find文章: 名称指南
除了总体大写的指导方针,这里是它在页面上的“事件” types成员的名称 :
用动词或动词短语来命名事件。
给事件名称一个前后的概念,用现在和过去时。 例如,在窗口closures之前引发的closures事件将被称为closures,而在closures该窗口之后引发的closures事件将被称为closures。
不要使用Before或After前缀或后缀来指示事件发生前后。
使用EventHandler后缀命名事件处理程序(委托用作事件types)。
在事件处理程序签名中使用两个名为sender和e的参数。
sender参数应该是Objecttypes的,而e参数应该是EventArgs的一个实例或inheritance。
使用EventArgs后缀命名事件参数类。
在这里,我会说一般事件的最佳指南,包括命名约定。
这是我通过的公约,简单地说:
- 事件名称通常以以-ing或-ed结尾的动词(Closing / Closed,Loading / Loaded)
- 声明事件的类应该有一个受保护的虚拟On [EventName],该类应该被其他类用来引发事件。 这个方法也可以被子类用来引发事件,也可以通过重载来修改事件产生逻辑。
- 对于“处理程序”的使用经常会感到困惑 – 为了保持一致性,所有代表都应该使用Handler作为后缀,尽量避免调用执行Handler处理程序的方法,
- 实现处理程序的方法的默认VS命名约定是EventPublisherName_EventName。
有趣的是,微软似乎用Visual Studio生成的事件处理程序名称来打破自己的命名约定。
请参阅: 事件命名指南(.NET Framework 1.1)
在.NET中使用事件多年后,我发现一个重点是需要在每次调用时检查事件是否为空处理程序。 我还没有看到一个活动代码,如果它为空,就不会调用事件。
我已经开始做的是在我创build的每个事件上放置一个虚拟处理程序,以保存执行空检查的需要。
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. } }
看起来不错,除了OnTick
并没有遵循典型的事件调用模型。 通常, On[EventName]
引发事件,例如
protected virtual void OnTick(EventArgs e) { if(Tick != null) Tick(this, e); }
考虑创build此方法,并将现有的“ OnTick
”方法重命名为“ StartTick
”,而不是直接从StartTick
调用Tick
,请从StartTick
方法调用OnTick(EventArgs.Empty)
。
你的情况可能是:
class Metronome { event Action Ticked; internalMethod() { // bla bla Ticked(); } }
以上sampple使用低于惯例,自我描述;]
活动来源:
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。 关键字event
是可选的,但可以区分“事件”和“callback”
事件监听器:
class AlarmManager { // pattern: NotifyXxx public NotifyLockStateChanged(bool state) { // ... } // pattern: [as above] public NotifyOpened(bool opened) { // OR public NotifyDoorOpened(bool opened) { // ... } }
和绑定[代码看起来人性化]
door.LockStateChanged += alarmManager.NotifyLockStateChanged; door.Moved += alarmManager.NotifyDoorOpened;
即使手动发送事件也是“可读的”。
alarmManager.NotifyDoorOpened(true);
有时候更多performance力可以是“动词+ ing”
dataGenerator.DataWaiting += dataGenerator.NotifyDataWaiting;
无论您select哪种约定,都要与其保持一致。