2008-12-04 12 views
65

Nie można wystrzelić zdarzenia w języku C#, do którego nie są dołączone żadne procedury obsługi. Dlatego przed każdym wywołaniem należy sprawdzić, czy zdarzenie ma wartość zerową.Automatyczne tworzenie pustych programów obsługi zdarzeń w języku C#

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

Chciałbym zachować mój kod tak czysty, jak to możliwe i pozbyć się tych zerowych kontroli. Nie sądzę, że wpłynie to na wydajność, przynajmniej nie w moim przypadku.

MyEvent(param1, param2); 

W tej chwili rozwiązuję ten problem, dodając pusty ręczny komunikat do każdego zdarzenia. To jest podatny na błędy, ponieważ trzeba pamiętać, aby zrobić itp

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

Czy istnieje sposób, aby generować pustych teleskopowe na wszystkich wydarzeniach z danej klasy automatycznie przy użyciu odbicia i trochę CLR magię?

+0

trick w przyjętym odpowiedź pozwoli uniknąć konieczności sprawdzenia zerowa, ale nie zapewni wątek Safty. zobacz tutaj: http://stackoverflow.com/questions/1131184/c-initializing-an-event-handler-with-a-dummy/1131204#1131204 –

Odpowiedz

135

Widziałem to na innym stanowisku, a nie bezczelnie ukradł go i wykorzystał je w dużej części mojego kodu odkąd:

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

* Jeśli ktoś zna pochodzenie tej techniki, proszę pisać w komentarzach . Naprawdę wierzę, że źródło otrzymuje należny kredyt.

(Edit: mam go z tego stanowiska Hidden Features of C#?)

+2

24 sekundy szybciej! – leppie

+3

Dodaj pustego delegata, właśnie tam! To nawet lepsze, niż miałem nadzieję. Dzięki! Przeczytam teraz wpis "ukrytych funkcji". –

+0

Tak - ten wpis jest bezcenny! Pamiętaj, aby często tam głosować. Zrobili nam wspaniałą usługę. – Dinah

6

Możesz napisać to jako:

MyEvent += delegate { }; 

Nie jestem pewien, co chcesz zrobić jest poprawny.

+0

Naprawdę wierzę, że dodanie pustego delegata do KAŻDEJ imprezy jest właściwą drogą, jak tworzyć aplikacje. Ale wierzę, że może być sytuacja, gdzie jest szybkie i łatwe rozwiązanie, jak sobie z tym poradzić. – TcKs

57

Notacja:

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

nie jest bezpieczeństwo wątków. Należy zrobić to w ten sposób:

EventHandler handler = this.MyEvent; 
if (null != handler) { handler(param1, param2); } 

Rozumiem, że jest to niepokoić, więc można zrobić metody pomocnika:

static void RaiseEvent(EventHandler handler, object sender, EventArgs e) { 
    if (null != handler) { handler(sender, e); } 
} 

a następnie zadzwonić:

RaiseEvent(MyEvent, param1, param2); 

Jeśli używają C# 3.0, możesz zadeklarować metodę pomocniczą jako metodę rozszerzenia:

static void Raise(this EventHandler handler, object sender, EventArgs e) { 
    if (null != handler) { handler(sender, e); } 
} 

a następnie zadzwonić:

MyEvent.Raise(param1, param2); 

Ponadto można tworzyć następne rozszerzenie/Helper dla innych metod obsługi zdarzeń. Na przykład:

static void Raise<TEventArgs>(this EventHandler<TEventArgs> handler, 
    object sender, TEventArgs e) where TEventArgs : EventArgs 
{ 
    if (null != handler) { handler(sender, e); } 
} 
+4

Korzystanie z metody rozszerzającej to piękne rozwiązanie. Powstrzymuję się, gdy pojawia się pojęcie inicjowania pustego delegata. – Greg

+0

Wow, myślałem, że '= delegat {}' był przydatny, gdy zobaczyłem go po raz pierwszy. To jest jednak +1 niesamowite. I tak oczywiste z perspektywy czasu, do cholery :) – shambulator

+0

+1 dla metod rozszerzenia –

2

To zły pomysł, że kod, który zużywa zdarzenie ma teraz oczekiwanie, że obiekt ze zdarzeniem został zakodowany z akcją domyślnie. Jeśli twój kod nigdy nie zostanie użyty nigdzie indziej przez nikogo innego, to myślę, że możesz temu zapobiec.

+0

Zgadzam się, tak jak komentowałem odpowiedź leppiego. +1 – TcKs

5

Nie trzeba kilka metod rozszerzenie dla różnych programów obsługi zdarzeń, wystarczy jeden:

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); 
    } 
} 
+0

Dziękuję Sandro! – vkelman

-1

Można użyć PostSharp się na czasie kompilacji dodać tę magię. To najlepszy sposób.

1

Deklaracje zdarzeń C# niestety zawierają wiele dobrze znanych problemów bezpieczeństwa i nieefektywności. I designed a number of extension methods on delegates to invoke them safely, and to register/unregister delegates in a thread-safe manner.

Twój stary kod zdarzenia świadomości:

if (someDelegate != null) someDelegate(x, y, z); 

Twój nowy kod:

someDelegate.Raise(x, y, z); 

Twój stary kod rejestracji zdarzeń:

event Action fooEvent; 
... 
lock (someDummyObject) fooEvent += newHandler; 

Twój nowy kod:

Action fooEvent; 
... 
Events.Add(ref fooEvent, newHandler); 

Nie jest wymagane blokowanie, nie są wstawiane obiekty obojętne z kompilatorem używane do blokowania zdarzeń.

2

W języku C# 6.0 nie ma potrzeby, aby przejść do któregokolwiek z tych długościach zrobić czek zerowej, dzięki warunkowego zerowej operatora ?.

The docs wyjaśniają, że powołanie MyEvent?.Invoke(...) kopie zdarzenie do zmiennej tymczasowej, wykonuje NULL sprawdź, a jeśli nie, null, wywołuje Invoke na kopii tymczasowej. Nie musi to być wątek bezpieczny pod każdym względem, ponieważ ktoś mógłby dodać nowe zdarzenie po skopiowaniu do zmiennej tymczasowej, która nie zostałaby wywołana. Gwarantuje to, że nie zadzwonisz pod numer Invoke na wartość null.

W skrócie:

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

public void DoSomething() { 
    Click?.Invoke(this, "foo"); 
} 
Powiązane problemy