Delegaty i zdarzenia
Część sekcji Programowanie obiektowe ścieżki C# w Coddy. Lekcja 43 z 70.
Delegat to typ reprezentujący odwołanie do metody. Możesz myśleć o nim jak o zmiennej przechowującej metodę zamiast danych. Dzięki temu możesz przekazywać metody jako argumenty, przechowywać je w kolekcjach i wywoływać je dynamicznie.
Deklarujesz delegata, określając sygnaturę metody, do której może się odwoływać:
public delegate void Notify(string message);
public class Publisher
{
public void SendAlert(string msg) => Console.WriteLine("Alert: " + msg);
}
// Użycie
Notify notifier = new Publisher().SendAlert;
notifier("System started"); // Alert: System startedZdarzenia opierają się na delegatach, aby implementować wzorzec publikuj-subskrybuj. Klasa wywołuje zdarzenie, a inne klasy mogą subskrybować je, aby otrzymywać powiadomienia. Słowo kluczowe event ogranicza sposób użycia delegata — subskrybenci mogą jedynie dodawać lub usuwać procedury obsługi, a nie wywoływać zdarzenia bezpośrednio.
public class Button
{
public event Action Clicked;
public void Click()
{
Clicked?.Invoke(); // Powiadom wszystkich subskrybentów
}
}
// Subskrybowanie
Button btn = new Button();
btn.Clicked += () => Console.WriteLine("Button was clicked!");
btn.Click(); // Przycisk został kliknięty!Operator += subskrybuje procedurę obsługi, a -= ją anuluje. Wzorzec ?.Invoke() bezpiecznie wywołuje zdarzenie tylko wtedy, gdy są subskrybenci. Zdarzenia są podstawą aplikacji GUI, silników gier i każdego systemu, w którym obiekty muszą reagować na zmiany bez ścisłego powiązania.
Wyzwanie
ŁatwyStwórzmy system monitorowania temperatury, który wykorzystuje delegaty i zdarzenia do powiadamiania subskrybentów za każdym razem, gdy zmieni się temperatura. To klasyczny przykład wzorca publikuj-subskrybuj, który zdarzenia umożliwiają w tak elegancki sposób.
Podzielisz kod na dwa pliki:
TemperatureMonitor.cs: Utwórz klasęTemperatureMonitorw przestrzeni nazwMonitoring, która będzie pełnić rolę wydawcy. Twój monitor powinien:- Mieć prywatne pole do przechowywania bieżącej temperatury
- Deklarować zdarzenie o nazwie
TemperatureChangedz użyciemAction<int>, które jest wywoływane za każdym razem, gdy temperatura zostanie zaktualizowana - Mieć właściwość
Temperature(int), której setter aktualizuje prywatne pole i wywołuje zdarzenie z nową wartością temperatury
?.Invoke(), żeby bezpiecznie wywoływać zdarzenie tylko wtedy, gdy są subskrybenci.Program.cs: W głównym pliku utwórz obiektTemperatureMonitori zasubskrybuj dwa różne procedury obsługi zdarzeńTemperatureChanged:- Jedna procedura obsługi, która wyświetla:
Alert: Temperature is now {value} degrees - Druga procedura obsługi, która wyświetla:
Logged: {value}
- Jedna procedura obsługi, która wyświetla:
Otrzymasz dwa dane wejściowe:
- Pierwsza wartość temperatury (liczba całkowita)
- Druga wartość temperatury (liczba całkowita)
Na przykład, jeśli dane wejściowe to 25 i 30, wynik powinien wyglądać następująco:
Alert: Temperature is now 25 degrees
Logged: 25
Alert: Temperature is now 30 degrees
Logged: 30Zauważ, że ustawienie właściwości Temperature automatycznie powiadamia wszystkich subskrybentów — monitor nie musi wiedzieć, kto nasłuchuje. Na tym polega piękno podejścia sterowanego zdarzeniami!
Spróbuj swoich sił
using System;
using Monitoring;
class Program
{
public static void Main(string[] args)
{
// Odczytaj dane wejściowe
int temp1 = Convert.ToInt32(Console.ReadLine());
int temp2 = Convert.ToInt32(Console.ReadLine());
// TODO: Utwórz instancję TemperatureMonitor
// TODO: Zarejestruj procedurę obsługi, która wypisuje: "Alert: Temperature is now {value} degrees"
// TODO: Zarejestruj kolejną procedurę obsługi, która wypisuje: "Logged: {value}"
// TODO: Ustaw temperaturę na temp1
// TODO: Ustaw temperaturę na temp2
}
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrznePrzestrzenie nazw i dyrektywyWprowadzenie do klas i obiektówSłowo kluczowe „this”Metody i parametryPola a właściwościKonstruktoryInicjalizatory obiektówPodsumowanie — prosty kalkulator4Dziedziczenie
Podstawowa składnia dziedziczenia (:)Słowo kluczowe 'base'Słowa kluczowe virtual i overrideKlasy sealedBazowa klasa 'object'Powtórzenie – hierarchia pracowników7Zaawansowane funkcje
Przeciążanie operatorówIndeksery (this[])Nadpisywanie ToString()Metody rozszerzającePodsumowanie – własna lista2Właściwości i składowe statyczne
Właściwości automatyczneWłaściwości tylko do odczytu i zapisuPola i metody statyczneKlasy statyczneSkładowe z ciałem wyrażeniowym5Polimorfizm i interfejsy
Polimorfizm czasu kompilacji a czasu wykonaniaInterfejs a klasa abstrakcyjnaWiele interfejsówJawne implementowanie interfejsówRzutowanie w górę i w dółPowtórka — kalkulator kształtów8Zaawansowane koncepcje OOP
Kompozycja zamiast dziedziczeniaTypy generyczne (klasy i metody)Delegaty i zdarzeniaAtrybuty i refleksjaIDisposable i instrukcja usingPodstawy wstrzykiwania zależności11Wzorce projektowe, część 2
Wzorzec poleceniaWzorzec adapteraWzorzec dekoratoraWzorzec metody szablonowejWzorzec stanuWzorzec kompozytu3Architektura klas
Dane instancji a dane statyczneSłowa kluczowe 'readonly' i 'const'Pola zapasowePowtórzenie — menedżer kont bankowych6Hermetyzacja
Modyfikatory dostępuWłaściwości w hermetyzacjiImplementacja ukrywania danychWzorce niezmiennościPowtórka — dane studentów9Argumenty zmiennej liczby
Słowo kluczowe „params”Parametry opcjonalneArgumenty nazwanePrzeciążanie metodPoćwicz samodzielnie: Kompilator C# online