Dav*_*eer 58 c# events delegates
我正在学习C#中的事件/代表.我可以问一下你对我所选择的命名/编码风格的看法(取自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();
}
}
Run Code Online (Sandbox Code Playgroud)
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
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");
}
}
Run Code Online (Sandbox Code Playgroud)
nb代码重构自http://www.codeproject.com/KB/cs/simplesteventexample.aspx
Yot*_*aXP 65
Microsoft实际上编写了大量的命名准则并将其放在MSDN库中.你可以在这里找到文章:名称指南
除了一般的大写指南之外,以下是它在" 名称类型成员 "页面上对"事件"的含义:
使用动词或动词短语命名事件.
使用现在和过去时,给事件名称赋予前后概念.例如,在关闭窗口之前引发的close事件将被称为Closing,窗口关闭后引发的close事件将被称为Closed.
请勿使用Before或After前缀或后缀来指示事件前和事件后.
使用EventHandler后缀命名事件处理程序(用作事件类型的委托).
在事件处理程序签名中使用名为sender和e的两个参数.
sender参数应为Object类型,e参数应为EventArgs的实例或从EventArgs继承.
使用EventArgs后缀命名事件参数类.
lc.*_*lc. 53
我要提几点:
Metronome.OnTick似乎没有正确命名.在语义上,"OnTick"告诉我它将在"Tick"时被调用,但这并不是真正发生的事情.我会称之为"Go".
然而,通常接受的模型是执行以下操作.OnTick是一种引发事件的虚拟方法.这样,您可以轻松地覆盖继承类中的默认行为,并调用基类来引发事件.
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
}
}
}
Run Code Online (Sandbox Code Playgroud)
另外,我知道这是一个简单的例子,但如果没有附加的监听器,你的代码就会抛出Tick(this, EventArgs.Empty).你应该至少包括一个空值守卫来检查听众:
if(Tick != null)
Tick(this, EventArgs.Empty);
Run Code Online (Sandbox Code Playgroud)
但是,如果侦听器在调用和调用之间取消注册,则在多线程环境中仍然容易受到攻击.最好的方法是首先捕获当前的侦听器并调用它们:
var tickEvent = Tick;
if(tickEvent != null)
tickEvent(this, EventArgs.Empty);
Run Code Online (Sandbox Code Playgroud)
我知道这是一个古老的答案,但由于它仍在收集投票,这里是C#6的做事方式.整个"guard"概念可以用条件方法调用替换,编译器确实在捕获侦听器方面做了Right Thing(TM):
Tick?.Invoke(this, EventArgs.Empty);
Run Code Online (Sandbox Code Playgroud)
Ben*_*jol 16
我想说一般事件的最佳指南,包括命名约定,就在这里.
这是我采用的惯例,简要说明: