Автоматическое создание пустых обработчиков событий C#
невозможно запустить событие в C#, к которому не привязаны обработчики. Поэтому перед каждым вызовом необходимо проверить, является ли событие null.
if ( MyEvent != null ) {
MyEvent( param1, param2 );
}
Я хотел бы, чтобы держать мой код как можно более чистым и избавиться от этих нулевых чеков. Я не думаю, что это сильно повлияет на производительность, по крайней мере, в моем случае.
MyEvent( param1, param2 );
прямо сейчас я решаю эту проблему, добавляя пустой встроенный обработчик к каждому событию вручную. Это подвержено ошибкам, так как мне нужно не забудьте сделать это и т. д.
void Initialize() {
MyEvent += new MyEvent( (p1,p2) => { } );
}
есть ли способ генерировать пустые обработчики для всех событий данного класса автоматически, используя отражение и некоторую магию CLR?
8 ответов:
Я видел это на другом посту и бесстыдно украл его и использовал его в большей части моего кода с тех пор:
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"); }
* если кто-то знает происхождение этой техники, пожалуйста, разместите его в комментариях. Я действительно верю в источник получения кредит.(Edit: я получил его от этой должности скрытые возможности C#?)
запись:
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 );
если вы используете C# 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 ); } }
вам не нужно несколько методов расширения для разных обработчиков событий, вам просто нужен один:
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); } }
в C# 6.0 нет необходимости переходить на любую из этих длин, чтобы выполнить проверку null, благодаря условному оператору null
?.
документы объяснить, что вызов
MyEvent?.Invoke(...)
копирует событие во временную переменную, выполняет проверку null, а если не null, вызываетInvoke
на временную копию. Это не обязательно потокобезопасно во всех смыслах, так как кто-то мог добавить новое событие после копирования во временную переменную, которая не будет вызвана. Это гарантия, что вы не будете звонитьInvoke
на нуль, хотя.короче:
public delegate void MyClickHandler(object sender, string myValue); public event MyClickHandler Click; public void DoSomething() { Click?.Invoke(this, "foo"); }
Это плохая идея в том, что код, который занимает сейчас есть надежда, что объект с событием был закодирован с действием по умолчанию. Если ваш код никогда не будет использоваться в другом месте кем-либо еще, то я думаю, вы можете уйти с ним.
объявления событий C# к сожалению, включают в себя ряд известных проблем безопасности и неэффективности. Я разработал ряд методов расширения для делегатов, чтобы безопасно вызывать их и регистрировать / отменять регистрацию делегатов потокобезопасным способом.
ваш старый код вызова событий:
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);
нет замок не требуется, чтобы компилятор вставлял фиктивные объекты, используемые для блокировки событий.