Delegacje

Delegacja pozwala utworzyć nowy typ danych, który definiuje sygnaturę metody. Obiekty delegacji mogą wywoływać metody zgodne z ich sygnaturą. Ogólna deklaracja delegacji:
[modyfikatory] delegate typ nazwaDelegacji ( ... ) ;

Np.


delegate int Policz (string s);    // <- Deklaracja (typu) delegacji mogącej wywoływać metody
...                                // pobierające parametr typu string i zwracające int.
Policz p = Policz( nazwaMetody );  // <- Utworzenie obiektu delegacji
p( ) ;                             // <- Wywołanie metody wskazywanej przez delegację

Jeśli utworzymy delegację typu void, to będzie ona mogła wskazywać i wywoływać wiele metod jednocześnie (delegacje inne niż void nie mogą rejestrować wielu metod, gdyż nie miało by sensu zwracanie wyniku wywołania wielu metod na raz). W takim wypadku rejestrujemy kolejne metody, które ma wywołać delegacja dzięki operatorowi +=, natomiast wyrejestrowujemy za pomocą -=. Przykładowo:



delegate void ListyFunkcji ( );       // Deklaracja delegacji typu void.

class A {
   public static void Main( ) {
      ListyFunkcji  listaInicjalizacyjna = null;  // <- Utworzenie obiektu delegacji
      listaInicjalizacyjna += przywitaj ;         // <- Zarejestrowanie dodatkowej funkcji
      ...                                         // Następnie obiekt listaInicjalizacyjna mogli byśmy
   }                                              // przekazać do funkcji lub wywołać.
   void  przywitaj() {...}                        // <- Funkcja zarejestrowana w delegacji.
} 

Zdarzenia

Obsługa zdarzeń w C# jest zwykle realizowana za pomocą delegacji. Platforma .NET sama definiuje delegacje przeznaczone do obsługi zdarzeń, można jednak zdefiniować własne. Zgodna z konwencją deklaracja delegacji zdarzenia jest następująca:
delegate void Zdarzenie( object źródłoZdarzenia, EventArgs opisZdarzenia );

Klasa opisująca zdarzenie powinna być klasą pochodną od System.EventArgs.

event: słowo kluczowe pozwalające zadeklarować obiekt zdarzenia. Dzięki niemu na delegacji (a przecież tym jest zdarzenie) będą mogły być przeprowadzane tylko operacje += oraz -= (inne klasy będą mogły rejestrować się w tej delegacji, ale tylko klasa deklarująca obiekt delegacji będzie mogła go wywoływać lub przeprowadzać inne operacje), np.


class Kontrolka {
   delegate void  Zdarzenie( ...) ;  // deklaracja typu delegacji (zdarzenia)
   public event Zdarzenie zd ;       // deklaracja obiektu zdarzenia. Inne klasy mogą
   ...                               // rejestrować swoje metody w delegacji zd.
   if( zd != null) zd( ...) ;        // wywołanie zdarzenia (wszystkich metod
}                                    // zarejestrowanych w zd)

Kontrolka k = new Kontrolka( );                 // Utworzenie kontrolki oraz
k.zd += new Kontrolka.Zdarzenie( mojaMetoda );  // zarejestrowanie w zdarzeniu pewnej
                                                // pewnej metody (mojaMetoda).

Metody anonimowe

Metody anonimowe pozwalają zapisać bloki kodu w miejscach gdzie oczekiwane są delegacje.

Przykładowo aby obsłużyć zdarzenie, nie musimy tworzyć deklaracji metody oraz delegacji wskazującej na tą metodę. Wystarczy utworzyć jedynie metodę anonimową:

Button przycisk = new Button( );                               // deklaracja przycisku
przycisk.Click += delegate( object sender, EventArgs e) {      // deklaracja metody anonimowej
   MessageBox.Show( ((Button)sender).Text );                   // ciało metody anonimowej
}

Ogólna postać deklaracji funkcji anonimowej: delegate( lista_parametrów ) { ... }
przy czym lista parametrów jest opcjonalna.

W rzeczywistości metoda anonimowa jest przekształcana przez kompilator w delegację i ta delegacja jest wstawiana w miejsce wystąpienia metody anonimowej. Przy czym, aby konwersja udała się musi wystąpić zgodność parametrów oraz zwracanej wartości. Metoda anonimowa może albo pobierać dokładnie te same parametry co delegacja, którą zastępuje, albo nie pobierać ich wcale (jeśli nie mamy zamiaru z nich korzystać; wtedy całkowicie rezygnujemy z listy parametrów).

Metody anonimowej możemy użyć też w sytuacji, gdy pewna funkcja spodziewa się jako parametru delegacji. W takim przypadku zamiast przekazywania obiektu delegacji wpisujemy w odpowiednie miejsce deklarację funkcji anonimowej, np.

delegate double Funkcja( double x );   // deklaracja pewnej delegacji
...
void  Wykonaj( Funkcja f ) {...}       // deklaracja funkcji pobierającej jako parametr delegację.
...
Wykonaj(  delegate { return 0; }  );   // wywołanie funkcji i podanie jako parametr metody anonimowej.

Wygodną cechą metod anonimowych jest także to, że mają one dostęp do zmiennych lokalnych i parametrów, których zasięg zawiera daną metodę anonimową. Np.

   delegate int GetValue( );
   class Test {
      void  FunctionA( GetValue  gv ) {...}
      void  FunctionB( int val ) {
         FunctionA( delegate{ return val; } );
      }
   }

Bardzo użyteczną cechą jest to, że nie tylko metody anonimowe mogą być przekształcane w delegacje. W miejscu, w którym jest to potrzebne, kompilator może przekształcić dowolną funkcję (dla której jest to możliwe) w delegację.

Przykładowo wywołanie: przycisk.Click += new EventHandler( funkcja );
może być zapisane jako: przycisk.Click += funkcja;

Tak samo możemy zrobić podczas przekazywania parametrów do funkcji - zamiast podawania delegacji, możemy wpisać nazwę metody, która zostanie przekształcona w delegację.


prev | Strona Główna | next

Copyright © 2006 Daniel Dusiński
Wszelkie prawa zastrzeżone