活动 – 命名约定和风格

我正在学习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哪种约定,都要与其保持一致。