Создавать пустые обработчики событий C # автоматически - PullRequest
66 голосов
/ 04 декабря 2008

Невозможно запустить событие в C #, к которому не прикреплены обработчики. Поэтому перед каждым вызовом необходимо проверять, является ли событие нулевым.

if ( MyEvent != null ) {
  MyEvent( param1, param2 );
}

Я бы хотел сохранить мой код как можно более чистым и избавиться от этих нулевых проверок. Я не думаю, что это сильно повлияет на производительность, по крайней мере, в моем случае.

MyEvent( param1, param2 );

Сейчас я решаю эту проблему, добавляя пустой встроенный обработчик к каждому событию вручную. Это подвержено ошибкам, так как я должен помнить, чтобы сделать это и т. Д.

void Initialize() {
  MyEvent += new MyEvent( (p1,p2) => { } );
}

Есть ли способ автоматически генерировать пустые обработчики для всех событий данного класса, используя отражение и некоторую магию CLR?

Ответы [ 8 ]

141 голосов
/ 04 декабря 2008

Я видел это в другом посте, беззастенчиво украл его и использовал в большей части своего кода с тех пор:

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");
}

* Если кто-нибудь знает происхождение этой техники, пожалуйста, оставьте ее в комментариях. Я действительно верю в источник, получающий должный кредит.

( Редактировать: Я получил это из этого поста Скрытые возможности C #? )

58 голосов
/ 04 декабря 2008

Обозначения:

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 ); }
}
6 голосов
/ 09 июня 2017

В C # 6.0 нет необходимости переходить на любую из этих длин для проверки нуля, благодаря условному оператору null ?.

Документы объясняют, что вызов MyEvent?.Invoke(...) копирует событие во временную переменную, выполняет проверку на ноль, а если не ноль, вызывает Invoke для временной копии. Это не обязательно поточно-ориентированный во всех смыслах, так как кто-то мог добавить новое событие после копирования во временную переменную, которая не была бы вызвана. Это гарантирует, что вы не будете звонить Invoke на ноль.

Короче говоря:

public delegate void MyClickHandler(object sender, string myValue);
public event MyClickHandler Click;

public void DoSomething() {
    Click?.Invoke(this, "foo");
}
6 голосов
/ 04 декабря 2008

Вы можете написать как:

MyEvent += delegate { };

Я не уверен, что вы хотите сделать правильно.

5 голосов
/ 31 марта 2009

Вам не нужно несколько методов расширения для разных обработчиков событий, вам нужен только один:

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);
  }
}
2 голосов
/ 04 декабря 2008

Это плохая идея, так как код, который потребляет событие, теперь ожидает, что объект с событием по умолчанию закодирован с действием. Если ваш код никогда не будет использоваться кем-то еще, то, я думаю, вам это сойдет с рук.

1 голос
/ 28 января 2011
К сожалению, объявления о событиях

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);

Блокировка не требуется, фиктивные объекты, вставленные компилятором, используются для блокировки событий.

0 голосов
/ 24 июля 2010

Вы можете использовать PostSharp, чтобы во время сборки добавить эту магию. Это лучший способ.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...