Автоматически создавать пустые обработчики событий С#
Невозможно запустить событие на С#, у которого нет связанных с ним обработчиков. Поэтому перед каждым вызовом необходимо проверить, является ли событие нулевым.
if ( MyEvent != null ) {
MyEvent( param1, param2 );
}
Я хотел бы сохранить мой код как можно более чистым и избавиться от этих нулевых проверок. Я не думаю, что это сильно повлияет на производительность, по крайней мере, не в моем случае.
MyEvent( param1, param2 );
В настоящее время я решаю это, добавляя пустой проводник для каждого события вручную. Это склонно к ошибкам, так как мне нужно помнить об этом и т.д.
void Initialize() {
MyEvent += new MyEvent( (p1,p2) => { } );
}
Есть ли способ генерировать пустые обработчики для всех событий данного класса, автоматически используя отражение и некоторую магию CLR?
Ответы
Ответ 1
Я видел это на другом посту и бесстыдно украл его и использовал его в большинстве своих кодов с тех пор:
public delegate void MyClickHandler(object sender, string myValue);
public event MyClickHandler Click = delegate {}; // add empty delegate!
//Let you do this:
public void DoSomething() {
Click(this, "foo");
}
//Instead of this:
public void DoSomething() {
if (Click != null) // Unnecessary!
Click(this, "foo");
}
* Если кто-то знает происхождение этой техники, отправьте ее в комментариях. Я действительно верю, что источник получает должный кредит.
(Изменить: Я получил его из этого сообщения Скрытые возможности С#?)
Ответ 2
Обозначение:
if ( MyEvent != null ) {
MyEvent( param1, param2 );
}
не является потокобезопасным. Вы должны сделать это следующим образом:
EventHandler handler = this.MyEvent;
if ( null != handler ) { handler( param1, param2 ); }
Я понимаю, что это беспокоит, поэтому вы можете использовать вспомогательный метод:
static void RaiseEvent( EventHandler handler, object sender, EventArgs e ) {
if ( null != handler ) { handler( sender, e ); }
}
а затем вызовите:
RaiseEvent( MyEvent, param1, param2 );
Если вы используете С# 3.0, вы можете объявить вспомогательный метод как метод расширения:
static void Raise( this EventHandler handler, object sender, EventArgs e ) {
if ( null != handler ) { handler( sender, e ); }
}
а затем вызовите:
MyEvent.Raise( param1, param2 );
Также вы можете создать следующие методы расширения/помощника для других обработчиков событий. Например:
static void Raise<TEventArgs>( this EventHandler<TEventArgs> handler,
object sender, TEventArgs e ) where TEventArgs : EventArgs
{
if ( null != handler ) { handler( sender, e ); }
}
Ответ 3
Вы можете написать так:
MyEvent += delegate { };
Я не уверен, что вы хотите сделать правильно.
Ответ 4
Вам не нужны несколько методов расширения для разных обработчиков событий, вам просто нужно:
public static class EventHandlerExtensions {
public static void Raise<T>(this EventHandler<T> handler, object sender, T args) where T : EventArgs {
if (handler != null) handler(sender, args);
}
}
Ответ 5
Это плохая идея в том, что код, который потребляет событие, теперь имеет ожидание того, что объект с событием был закодирован с действием по умолчанию. Если ваш код никогда не будет использоваться нигде кем-либо другим, я думаю, вы можете с ним справиться.
Ответ 6
Объявление событий С#, к сожалению, включает в себя ряд хорошо известных проблем безопасности и неэффективности. Я разработал ряд методов расширения для делегатов, чтобы безопасно их вызывать, а также для регистрации/отмены регистрации делегатов поточно-безопасным способом.
Ваш старый код для создания событий:
if (someDelegate != null) someDelegate(x, y, z);
Ваш новый код:
someDelegate.Raise(x, y, z);
Ваш старый регистрационный код события:
event Action fooEvent;
...
lock (someDummyObject) fooEvent += newHandler;
Ваш новый код:
Action fooEvent;
...
Events.Add(ref fooEvent, newHandler);
Никакой блокировки не требуется, никакие фиктивные объекты, вставленные компилятором, используемые для блокировки событий.
Ответ 7
В С# 6.0 нет необходимости переходить к любой из этих длин, чтобы выполнить нулевую проверку, благодаря условному оператору null ?.
Документы объясняют, что вызов MyEvent?.Invoke(...)
копирует событие во временную переменную, выполняет нулевую проверку, а если не null, вызывает Invoke
на временной копии. Это не обязательно поточно-безопасное во всех смыслах, так как кто-то мог добавить новое событие после копии во временную переменную, которая не была бы вызвана. Это гарантирует, что вы не назовете Invoke
на нулевое значение.
Короче:
public delegate void MyClickHandler(object sender, string myValue);
public event MyClickHandler Click;
public void DoSomething() {
Click?.Invoke(this, "foo");
}
Ответ 8
Вы можете использовать PostSharp для добавления времени на добавление этой магии. Это лучший способ.