2008-12-04 3 views
65

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

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

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

MyEvent(param1, param2); 

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

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

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

+0

трюк в принятом ответе будет избежать необходимости проверять нуль, но не обеспечит нить Сафти. см. здесь: http://stackoverflow.com/questions/1131184/c-initializing-an-event-handler-with-a-dummy/1131204#1131204 – 2010-04-14 21:41:19

ответ

135

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

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: Я получил его с этого поста Hidden Features of C#?)

+2

24 секунды быстрее! – leppie 2008-12-04 13:46:17

+3

Добавить пустой делегат, прямо здесь! Это даже лучше, чем я надеялся. Благодаря! Я сейчас буду читать «скрытые функции». – 2008-12-04 14:02:55

+0

Да - Этот пост бесценен! Обязательно проголосуйте там часто. Они сделали нам все отличное обслуживание. – Dinah 2008-12-04 14:04:27

6

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

MyEvent += delegate { }; 

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

57

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

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

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

5

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

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

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

1

Объявление о событиях на C#, к сожалению, включает в себя ряд известных проблем безопасности и неэффективности. I designed a number of extension methods on delegates to invoke them safely, and to register/unregister delegates in a thread-safe manner.

Ваш старый код события осведомленности:

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

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

2

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

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

Короче:

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

public void DoSomething() { 
    Click?.Invoke(this, "foo"); 
} 
Смежные вопросы