Zdarzenia oraz delegaty (tak to chyba się tłumaczy?) pełnią podobną funkcje w C#. Jaka jest jednak różnica? Przyjrzyjmy się sposobowi ich użycia:
class Program { public static event EventHandler SimpleEvent = null; public static EventHandler SimpleDelegate = null; static void Main(string[] args) { SimpleEvent+=new EventHandler(EventMethod); SimpleDelegate += new EventHandler(DelegateMethod); SimpleEvent(null, null); SimpleDelegate(null, null); } static void DelegateMethod(object sender, EventArgs e) { } static void EventMethod(object sender, EventArgs e) { } }
Deklaracja różni się wyłącznie słowem kluczowym. Użycie jest identyczne. Istnieje jednak kilka różnic pomiędzy tymi mechanizmami:
- Deklaracja w interfejsie
Zdarzenia (tak jak właściwości i metody) mogą być zadeklarowane w interfejsie i potem wymuszone w klasie. W przypadku czystych delagate jest to niemożliwe (interfejs może zawierać wyłącznie właściwości, zdarzenia, metody). Poniższa konstrukcja jest wiec jak najbardziej poprawna:
interface IExample { event EventHandler SampleEvent; }
Z kolei poniższa deklaracja nie zostanie skompilowana:
interface IExample { EventHandler SampleEvent; }
- Wywołania
Zdarzenia mogą być wyłącznie wywoływane w klasie, w której zostały zadeklarowane. Niemożliwe jest odpalenie zdarzenia z klasy pochodnej np:
class BaseClass { public event EventHandler SampleEvent=null; } class SubClass:BaseClass { private void SomeMethod() { if(SampleEvent!=null) SampleEvent(null,null); // błąd! } }
W przypadku delegat powyższa konstrukcja jest poprawna.
- Słowa kluczowe Add \ Remove
Zdarzenia dostarczają dwa nowe słowa kluczowe Add oraz Remove. Są to odpowiedniki dla Get\Set, znanych z właściwości. Przykład (źródło: MSDN):
public class PropertyEventsSample { private Hashtable eventTable = new Hashtable(); public event MyDelegate1 Event1 { add { eventTable["Event1"] = (MyDelegate1)eventTable["Event1"] + value; } remove { eventTable["Event1"] = (MyDelegate1)eventTable["Event1"] - value; } } public event MyDelegate1 Event2 { add { eventTable["Event2"] = (MyDelegate1)eventTable["Event2"] + value; } remove { eventTable["Event2"] = (MyDelegate1)eventTable["Event2"] - value; } } public event MyDelegate2 Event3 { add { eventTable["Event3"] = (MyDelegate2)eventTable["Event3"] + value; } remove { eventTable["Event3"] = (MyDelegate2)eventTable["Event3"] - value; } } public event MyDelegate3 Event4 { add { eventTable["Event4"] = (MyDelegate3)eventTable["Event4"] + value; } remove { eventTable["Event4"] = (MyDelegate3)eventTable["Event4"] - value; } } public event MyDelegate3 Event5 { add { eventTable["Event5"] = (MyDelegate3)eventTable["Event5"] + value; } remove { eventTable["Event5"] = (MyDelegate3)eventTable["Event5"] - value; } } public event MyDelegate4 Event6 { add { eventTable["Event6"] = (MyDelegate4)eventTable["Event6"] + value; } remove { eventTable["Event6"] = (MyDelegate4)eventTable["Event6"] - value; } } }
- Sygnatura
Oczywiście zdarzenia mogą mieć dowolną sygnaturę, ale przyjęło się, że powinna wyglądać następująco:
void Name(object sender, EventArgs args)
Argumenty to dowolna klasa dziedzicząca po EventArgs.
Odnoście “Deklaracja w interfejsie”, dla ścisłości nie jest to problem delagate, a tego że w interface NIE mogą się znajdować pola, zatem deklaracja delagate JEST możliwa (np. za pomocą właściwości, a nie za pomocą pola)
Hej, delegata można jak najbardziej użyć w interfejsie, ale musi być on zdefiniowany jako właściwość. Tak jak napisałeś, interface nie może zawierać definicji pól. Skądinąd ciekawy post:) pozdrawiam
Faktycznie, trochę nieprecyzyjnie się wyraziłem. Chodziło oczywiście o taka samą deklaracje jak w przypadku event i pokazane różnicy między nimi:)
Pozdrawiam
Piotr
Jeśli mnie pamięć nie myli, jest też jakaś różnica na korzyść delegatów w przypadku aplikacji wielowątkowej. Niestety nie odgrzebię teraz szczegółów.
Zabrakło mi tu wyraźniejszego wytłumaczenia, czym jest zdarzenie: to prywatna delegata opakowana w publiczne (w zasadzie wszystko jedno, jakie) akcesory add i remove. Kod
public event EventHandler MyEvent
Kompiluje się identycznie jak:
private EventHandler _myEvent;
public event EventHandler MyEvent
{
[MethodImpl(MethodImplOptions.Synchronized)]
add
{
_myEvent = (EventHandler)Delegate.Combine(_myEvent, value);
}
[MethodImpl(MethodImplOptions.Synchronized)]
remove
{
_myEvent = (EventHandler)Delegate.Remove(_myEvent, value);
}
}
@twk – jak widać powyżej – akcesory są Synchronized, czyli wykonują się w sekcji krytycznej. Ale to raczej różnica na korzyść zdarzeń, a nie gołej delegaty.