C# 事件 - 命名约定和样式
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/724085/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Events - naming convention and style
提问by Dave Mateer
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)?
我正在学习 C# 中的事件/委托。我能否就我选择的命名/编码风格(取自 Head First C# 一书)征求您的意见?
Am teaching a friend about this tomorrow, and am trying to come up with the most elegant way of explaining the concepts. (thought the best way to understand a subject is to try and teach it!)
明天正在教一个朋友这个,并试图想出解释这些概念的最优雅的方式。(认为理解一门学科的最好方法是尝试教它!)
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");
}
}
n.b. Code is refactored from http://www.codeproject.com/KB/cs/simplesteventexample.aspx
nb 代码重构自http://www.codeproject.com/KB/cs/simplesteventexample.aspx
采纳答案by lc.
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.
Metronome.OnTick 似乎没有正确命名。从语义上讲,“OnTick”告诉我它会在“Tick”s 时被调用,但这并不是真正发生的事情。我会称它为“Go”。
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.
然而,通常接受的模型将执行以下操作。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
}
}
}
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:
另外,我知道这是一个简单的示例,但是如果没有附加侦听器,您的代码将抛出Tick(this, EventArgs.Empty)
. 您至少应该包含一个空保护来检查侦听器:
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:
我知道这是一个旧答案,但由于它仍在收集投票,这里是 C# 6 的做事方式。整个“守卫”概念可以用条件方法调用代替,并且编译器确实在捕获侦听器方面做了正确的事情(TM):
Tick?.Invoke(this, EventArgs.Empty);
回答by Adam Robinson
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
看起来不错,除了OnTick
不遵循典型的事件调用模型的事实。通常,一次On[EventName]
引发事件,例如
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.
考虑创建此方法,并将现有的“ OnTick
” 方法重命名为“ StartTick
”,而不是Tick
直接从调用,而是从该方法StartTick
调用。OnTick(EventArgs.Empty)
StartTick
回答by sipwiz
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.
我在 .Net 中使用事件多年后发现的一点是,每次调用时都需要重复检查事件是否有空处理程序。我还没有看到一段实时代码,它可以执行任何操作,但如果事件为空则不会调用该事件。
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.
}
}
回答by YotaXP
Microsoft has actually written extensive set of naming guidelines and put it in the MSDN library. You can find the articles here: Guidelines for Names
Microsoft 实际上已经编写了大量的命名指南并将其放入 MSDN 库中。您可以在此处找到这些文章:名称指南
Aside from the general capitalization guidelines, here is what it has for 'Events' on the page Names of Type Members:
除了一般的大写准则外,以下是类型成员名称页面上的“事件” :
Do name events with a verb or a verb phrase.
Do give event names a concept of before and after, using the present and past tense. For example, a close event that is raised before a window is closed would be called Closing and one that is raised after the window is closed would be called Closed.
Do not use Before or After prefixes or suffixes to indicate pre and post events.
Do name event handlers (delegates used as types of events) with the EventHandler suffix.
Do use two parameters named sender and e in event handler signatures.
The sender parameter should be of type Object, and the e parameter should be an instance of or inherit from EventArgs.
Do name event argument classes with the EventArgs suffix.
用动词或动词短语命名事件。
使用现在和过去时,给事件命名之前和之后的概念。例如,在窗口关闭之前引发的关闭事件将称为 Closing,而在窗口关闭之后引发的事件将称为 Closed。
不要使用 Before 或 After 前缀或后缀来表示前后事件。
使用 EventHandler 后缀命名事件处理程序(用作事件类型的委托)。
请在事件处理程序签名中使用两个名为 sender 和 e 的参数。
sender 参数应该是 Object 类型,e 参数应该是 EventArgs 的实例或继承自 EventArgs。
使用 EventArgs 后缀命名事件参数类。
回答by Benjol
I would say the best guide to events in general, including naming conventions, is here.
我会说一般事件的最佳指南,包括命名约定,在这里。
It is the convention I have adopted, briefly:
这是我采用的约定,简要地说:
- Events names are typically terminated with a verb ending with -ing or -ed (Closing/Closed, Loading/Loaded)
- The class which declares the event should have a protected virtual On[EventName] which should be used by the rest of the class for raising the event. This method can be also used by subclasses to raise the event, and also overloaded to modify the event-raising logic.
- There is often confusion about the use of 'Handler' - for coherence, all delegates should be postfixed with Handler, try to avoid calling the methods which implement the Handler 'handlers'
- The default VS naming convention for the method which implements the handler is EventPublisherName_EventName.
- 事件名称通常以 -ing 或 -ed 结尾的动词结尾(Closing/Closed, Loading/Loaded)
- 声明事件的类应该有一个受保护的虚拟 On[EventName],类的其余部分应该使用它来引发事件。这个方法也可以被子类用来引发事件,也可以重载来修改事件引发逻辑。
- 'Handler' 的使用经常存在混淆 - 为了连贯性,所有委托都应该后缀为 Handler,尽量避免调用实现 Handler 'handlers' 的方法
- 实现处理程序的方法的默认 VS 命名约定是 EventPublisherName_EventName。
回答by Benjol
Interesting how Microsoft seems to break its own naming conventions with Visual Studio generated event handler names.
有趣的是,Microsoft 似乎如何使用 Visual Studio 生成的事件处理程序名称打破自己的命名约定。
回答by Grzegorz Dev
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'
顺便提一句。关键字event
是可选的,但可以区分“事件”和“回调”
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"
有时更具表现力的可以是“动词 + ing”
dataGenerator.DataWaiting += dataGenerator.NotifyDataWaiting;
Whichever convention you choose, be consistent with it.
无论您选择哪种约定,都要与其保持一致。