События-соглашение об именах и стиль
я узнаю о событиях / делегатах в 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();
}
}
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. код рефакторируется из http://www.codeproject.com/KB/cs/simplesteventexample.aspx
7 ответов:
есть несколько моментов, которые я бы отнес:
метроном.OnTick, похоже, не назван правильно. Семантически " OnTick "говорит мне, что он будет называться, когда он"тикает", но это не совсем то, что происходит. Я бы назвал это "идти" вместо этого.
типично принятая модель, однако было бы сделать следующее.
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 } } }
кроме того, я знаю, что это простой пример, но если нет никаких слушателей, ваш код будет бросать на
Tick(this, EventArgs.Empty)
. Вы должны по крайней мере включить нулевой охранник для проверки слушателей:if(Tick != null) Tick(this, EventArgs.Empty);
однако это все еще уязвимо в многопоточной среде, если прослушиватель незарегистрирован между защитой и вызовом. Лучше всего было бы сначала захватить текущих слушателей и назвать их:
var tickEvent = Tick; if(tickEvent != null) tickEvent(this, EventArgs.Empty);
I знайте, что это старый ответ, но поскольку он все еще собирает upvotes, вот способ c# 6 делать вещи. Вся концепция "guard" может быть заменена условным вызовом метода, и компилятор действительно делает правильную вещь (TM) в отношении захвата слушателей:
Tick?.Invoke(this, EventArgs.Empty);
Microsoft фактически написала обширный набор рекомендаций по именованию и поместила его в библиотеку MSDN. Здесь вы можете найти статьи: рекомендации для Имен
помимо общих принципов капитализации, вот что он имеет для "событий" на странице имена членов типа:
назовите события с помощью глагола или глагола фраза.
дайте имена событий понятие до и после, используя настоящее время и в прошедшем времени. Например, закрыть событие, которое возникает перед окном закрыто будет называться закрытие и тот, который поднимается после окна закрытый будет называться закрытым.
не использовать до или после префиксов или суффиксы для обозначения pre и post события.
Do name обработчики событий (используются делегаты как типы событий) с Суффиксом EventHandler в.
используйте два параметра с именем sender и е в обработчик события подписывание.
параметр sender должен иметь тип Объект, и параметр e должен быть экземпляр или наследование от EventArgs.
назовите классы аргументов событий с помощью суффикс EventArgs.
Я бы сказал, что лучшим руководством для событий в целом, включая соглашения об именах, является здесь.
Это конвенция, которую я принял, кратко:
- имена событий обычно заканчиваются глаголом, заканчивающимся на-ing или-ed (закрытие/закрыто, загрузка/загружено)
- класс, который объявляет событие, должен иметь защищенный виртуальный On[EventName], который должен использоваться остальной частью класса для вызова события. Этот метод может быть также используется подклассами для вызова события, а также перегружается для изменения логики вызова события.
- часто возникает путаница в использовании "обработчика" - для согласованности все делегаты должны быть зафиксированы с помощью обработчика, старайтесь избегать вызова методов, которые реализуют обработчик "обработчики"
- соглашение по умолчанию VS именования для метода, который реализует обработчик EventPublisherName_EventName.
интересно, как Microsoft, похоже, нарушает свои собственные соглашения об именах с именами обработчиков событий, созданных Visual Studio.
посмотреть: рекомендации по именованию событий (.NET Framework 1.1)
точка, которую я нашел после использования событий в .Net в течение многих лет, - это повторяющаяся необходимость проверять событие для нулевого обработчика при каждом вызове. Я еще не видел кусок живого кода, который делает что-либо, но не вызывает событие, если оно равно null.
то, что я начал делать, это поставить фиктивный обработчик на каждое событие, которое я создаю, чтобы сохранить необходимость сделать нулевую проверку.
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); }
подумайте о создании этого метода и переименовании существующего "
OnTick
"метод to"StartTick
", и вместо вызоваTick
непосредственно сStartTick
, называютOnTick(EventArgs.Empty)
СStartTick
метод.
в вашем случае это может быть:
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); } }
кстати. ключевое слово
event
является необязательным, но позволяет отличать "события" от "обратных вызовов"событий слушатель:
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);
иногда больше выразительным может быть "глагол + ing и"
dataGenerator.DataWaiting += dataGenerator.NotifyDataWaiting;
какое бы соглашение вы ни выбрали, будьте последовательны с ним.