Delegaty i zdarzenia

Autor podstrony: Krzysztof Zajączkowski

Stronę tą wyświetlono już: 3468 razy

Wstęp

Wyobraźmy sobie taką oto sytuację, że utworzony został pewien zbiór obiektów geometrycznych, z których każdy może się przemieszczać w pewnej funkcji czasu i gdy piszę, że może to znaczy że obiekt ten nie musi koniecznie się przemieszczać. A więc, jeżeli przemieszczenie obiektu jest zerowe to obiekt się nie przemieszcza, a jeżeli jest różne od zera to się przemieszcza. Przemieszczenie obiektu nazwijmy sobie zdarzeniem, w reakcji na to zdarzenie obiekt wywołuje pewien zbiór funkcji, tak zwanych delegatów, które mają za zadanie poinformować wszystkie inne obiekty o tym, że ten konkretny obiekt się przemieścił. W odpowiedzi na to każdy z pozostałych obiektów sprawdza, czy nie nastąpiła jego kolizja z obiektem wysyłającym zgłoszenie zdarzenia.

Najczęściej zdarzenia będą spotykane przy tworzeniu programów okienkowych w C#, np zdarzenie Click dla przycisku.

Delegaty i zdarzenia

W poniższym przykładzie wykorzystałem wcześniej już opisywaną na stronie Programowanie → Podstawy C# → Obsługa operatorów struktur i klas definicję klasy Point2D. Poniżej zamieszczam przykład zastosowania delegacji i zdarzeń:

public delegate void collision(circle c); // tworzenie typu delegata public class circle : Point2D { protected uint ray; public event collision col; // tworzenie zdarzenia, które będzie wywoływane, gdy obiekt będzie się przemieszczał public uint Ray { get { return ray; } set { ray = value; } } public circle() { ray = 0; col = null; } public circle(uint ray, double x, double y) : base(x, y) { this.ray = ray; col = null; } public void move(double dx, double dy) { X += dx; Y += dy; if (dx != 0 || dy != 0) { if (col != null) { col(this); // tutaj wywoływane będą wszystkie funkcje przypisane do zdarzenia } } } public void collision_reaction(circle c) // ta metoda będzie wykorzystywana do generowania reakcji na zdarzenie { if (Math.Sqrt((c.X - X) * (c.X - X) + (c.Y - Y) * (c.Y - Y)) <= c.Ray + ray) { Console.WriteLine("okrąg o współrzędnych: x = " + c.X + "; y = " + c.Y + "; i promieniu r = " + c.Ray); Console.WriteLine("koliduje z okrągiem o współrzędnych: x = " + X + "; y = " + Y + "; i promieniu r = " + Ray); } else { Console.WriteLine("brak zderzenia"); } } }

W powyższym kodzie utworzony został typ delegacji collision (kolizja), który określa jaki typ metod może on przechowywać, ponieważ w przypadku, gdy dany delegat zwraca typ void to taki delegat przechowuje listę metod, które w reakcji na jego wywołanie kolejno będą wywoływane. Utworzenie zdarzenia ma miejsce wewnątrz klasy i jest ono ściśle powiązane z utworzeniem obiektu delegacji z wykorzystaniem słowa kluczowego event (zdarzenie). Klasa circle będzie wywoływała zdarzenie col, gdy dojdzie do przemieszczenia tegoż obiektu w celu obsłużenia wykrycia i reakcji na ewentualną kolizję.

A oto kod, który wywołuje odpowiedź na zdarzenie col:

var c1 = new circle(10, 100, 0); var c2 = new circle(10, 10, 0); c1.col += c2.collision_reaction; // dodanie zdarzenia do obiektu c1 jako metody z obiektu c2 c2.col += c1.collision_reaction; // to samo, tylko że na odwrót c1.move(-93, 0); // przemieszczenie obiektu wywoła pierwsze zdarzenie mówiące o zderzeniu c1 z c2 c2.move(100,200); // przemieszczenie obiektu wywoła drugie zdarzenie mówiące, że obiekty się nie zderzyły Console.ReadLine();

Wynik działania powyższego kodu jest następujący:

okrąg o współrzędnych: x = 7; y = 0; i promieniu r = 10
koliduje z okrągiem o współrzędnych: x = 10; y = 0; i promieniu r = 10
brak zderzenia

Możliwe jest również usunięcie danej metody z listy zdarzeń za pomocą operatora -=. Dodatkowo można utworzyć delegację, która zwraca jakąś wartość, w takim przypadku obiekt takiego delegata nie może przyjmować listy metod a jedynie jedną metodę.