Ludzie pragną czasami się rozstawać, żeby móc tęsknić, czekać i cieszyć się z powrotem.
Ponieważ jednak Write jest w rzeczywistości delegatem, tak naprawdę wywołana jest metoda, do której delegat zawiera referencję, a więc WriteString. Wynikiem tego prostego przykładu jest:
W CallDelegate
W WriteString: Witaj w delegacie
Warto zwrócić uwagę na fakt, że jako pierwsza wykonywana jest metoda CallDelegate, a dopiero po niej następuje WriteString.
98
Języki .NET
Delegat może być
Delegaty mogą być znacznie bardziej skomplikowane, niż wskazuje na łączony z innymi
to powyższy przykład. Mogą być na przykład łączone tak, by wywoła-delegatami
nie pojedynczego delegata wiązało się z wywołaniem dwóch lub większej liczby delegatów, które zawiera pierwszy z nich. Jednak nawet proste delegaty mogą być przydatne. Dzięki zapewnieniu bezpiecznego sposobu przekazania referencji do metody ułatwiają wykonanie tej czynności w sposób o wiele mnie ryzykowny niż w przypadku wcze-
śniejszych języków.
.NET Framework
Jedno z bardziej popularnych zastosowań delegatów obejmuje obsługę oraz C#
zdarzeń. Na przykład w GUI kliknięcia myszką użytkownika, naciśnięcia zapewniają
klawiszy czy inne formy danych wejściowych mogą być przyjmowane obsługę zdarzeń
jako zdarzenia; zdarzenia są także użyteczne w innych kontekstach.
opartÄ… na
delegatach
Ponieważ zdarzenia są tak popularne, C# oraz .NET Framework zapewniają specjalną pomoc przy wykorzystywaniu delegatów w obsłudze zdarzeń w spójny sposób. Delegat, którego używa zdarzenie, nazywany jest programem obsługi zdarzeń (ang. event handler), choć w rzeczywistości jest to normalny delegat. Platforma .NET Framework definiuje jednak dwie konwencje dla takich programów obsługi zdarzeń:
„ Program obsługi zdarzeń nie zwraca wartości, co oznacza, że typem zwracanej wartości jest void.
„ Program obsługi zdarzeń zawsze przyjmuje dwa argumenty.
Pierwszy argument, identyfikujący źródło zdarzenia, jest zgodnie z konwencją nazywany sender i jest typu System.Object (w C#
jest po prostu typem object, który jest aliasem System.Object).
Dzięki temu odbiorca zdarzenia może łatwo odpowiedzieć do dowolnego obiektu, który zdarzenie spowodował, na przykład wywołując metodę w tym obiekcie. Drugi argument, zawierający dane, które źródło przekazuje, kiedy wywołuje program obsługi zdarzeń, jest tradycyjnie zwany e i jest typu System.EventArgs bądź też typu dziedziczącego po System.EventArgs.
Delegaty
Poniżej znajduje się przykładowa deklaracja programu obsługi zdarzeń: wykorzystywane
public delegate void MyEventHandler(object sender, MyEventArgs e); w zdarzeniach
przestrzegajÄ…
W powyższym przykładzie typ MyEventArgs musi pochodzić od pewnych
konwencji
System.EventArgs i musi rozszerzać ten typ bazowy w taki sposób, by mógł on służyć do przekazywania danych zdarzenia. Dla zdarzeń, które nie generują żadnych specyficznych informacji, typem służącym
C#
99
do danych przekazywanych do programu obsługi zdarzeń może być po prostu System.EventArgs (nawet jeśli żadne dane nie są przekazywane, konwencja dotycząca zdarzeń wymaga, by ten parametr nadal pojawiał się w wywołaniu). Ponieważ zdarzenia często nie mają żadnych specyficznych dla nich danych, przestrzeń nazw System obejmuje także wbudowany typ — EventHandler. Typ ten jest po prostu delegatem z dwoma argumentami: obiektem wraz z System.EventArgs.
Kiedy odpowiedni program obsługi zdarzeń (to znaczy delegat zgodny C# dostarcza
z opisanymi powyżej konwencjami) zostanie zadeklarowany, możliwe słowo kluczowe
jest definiowanie zdarzenia z użyciem tego delegata. Poniżej znajduje event, które służy
do deklaracji
się przykład takiej sytuacji:
zdarzeń
public event MyEventHandler MyEvent;
Jak pokazuje powyższy przykład, deklaracja musi składać się ze słowa kluczowego event, natomiast typ musi być typem delegata.
Znając już podstawy, najłatwiej będzie zrozumieć sposób działania zdarzeń dzięki przykładowi. Zaprezentowany poniżej listing zawiera trzy klasy: EventSource, definiującą zdarzenie, EventSink, otrzymującą i odpowiadającą na zdarzenie, oraz EventMain, tworzącą obiekty dwóch pierwszych klas, a następnie generującą zdarzenie. Poniżej znajduje się odpowiedni kod:
public class EventSource
{
public event System.EventHandler EventX;
public void RaiseEventX()
{
if (EventX != null)
EventX(this, System.EventArgs.Empty);
}
}
public class EventSink
{
public EventSink(EventSource es)
{
es.EventX += new
System.EventHandler(ReceiveEvent);
}
public void ReceiveEvent(object sender,
System.EventArgs e)
100
Języki .NET
{
System.Console.WriteLine("EventX wywołane");
}
}