Делегаты и события
Часть раздела Объектно-ориентированное программирование путешествия по C# на Coddy. Урок 43 из 70.
delegate — это тип, представляющий ссылку на метод. Представь его как переменную, которая хранит метод, а не данные. Это позволяет передавать методы в качестве аргументов, хранить их в коллекциях и вызывать динамически.
Вы объявляете delegate, указывая сигнатуру метода, на который он может ссылаться:
public delegate void Notify(string message);
public class Publisher
{
public void SendAlert(string msg) => Console.WriteLine("Alert: " + msg);
}
// Использование
Notify notifier = new Publisher().SendAlert;
notifier("System started"); // Оповещение: Система запущенаСобытия основаны на делегатах и используются для реализации шаблона издатель–подписчик. Класс вызывает событие, а другие классы могут подписаться, чтобы получать уведомления. Ключевое слово event ограничивает способы использования делегата — подписчики могут только добавлять или удалять обработчики, но не вызывать событие напрямую.
public class Button
{
public event Action Clicked;
public void Click()
{
Clicked?.Invoke(); // Уведомить всех подписчиков
}
}
// Подписка
Button btn = new Button();
btn.Clicked += () => Console.WriteLine("Button was clicked!");
btn.Click(); // Кнопка была нажата!Оператор += подписывает обработчик, а -= отменяет подписку. Шаблон ?.Invoke() безопасно вызывает событие, только если есть subscribers. События имеют фундаментальное значение для приложений с графическим интерфейсом, игровых движков и любых систем, в которых объектам нужно реагировать на изменения без жёсткой связанности.
Задание
ЛегкоДавайте создадим систему мониторинга температуры, которая использует делегаты и события, чтобы уведомлять subscribers всякий раз, когда температура изменяется. Это классический пример шаблона publish-subscribe, который event позволяет реализовать особенно элегантно.
Вы организуете код в двух файлах:
TemperatureMonitor.cs: создайтеTemperatureMonitorclass в namespaceMonitoring, который выступает в роли издателя. Ваш monitor должен:- иметь private field для хранения текущей температуры
- объявлять event с использованием
Action<int>под названиемTemperatureChanged, который срабатывает всякий раз, когда температура обновляется - иметь свойство
Temperature(int), setter которого обновляет private field и вызывает event с новым значением температуры
?.Invoke(), чтобы безопасно вызывать event только при наличии subscribers.Program.cs: в основном файле создайтеTemperatureMonitorи подпишите два разных handler на его eventTemperatureChanged:- один handler, который выводит:
Alert: Temperature is now {value} degrees - другой handler, который выводит:
Logged: {value}
- один handler, который выводит:
Вы получите два входных значения:
- первое значение температуры (целое число)
- второе значение температуры (целое число)
Например, если входные значения — 25 и 30, результат должен быть таким:
Alert: Temperature is now 25 degrees
Logged: 25
Alert: Temperature is now 30 degrees
Logged: 30Обратите внимание, как установка свойства Temperature автоматически уведомляет всех subscribers — monitor не нужно ничего знать о том, кто слушает. В этом и заключается преимущество event-driven подхода!
Попробуйте сами
using System;
using Monitoring;
class Program
{
public static void Main(string[] args)
{
// Прочитать ввод
int temp1 = Convert.ToInt32(Console.ReadLine());
int temp2 = Convert.ToInt32(Console.ReadLine());
// TODO: Создать экземпляр TemperatureMonitor
// TODO: Subscribe a handler that prints: "Alert: Temperature is now {value} degrees"
// TODO: Подписать другой обработчик, который выводит: "Logged: {value}"
// TODO: Установить температуру в temp1
// TODO: Установить температуру в temp2
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыПространства имен и директивыВведение в классы и объектыКлючевое слово 'this'Методы и параметрыПоля и свойстваКонструкторыИнициализаторы объектовИтоги — Простой калькулятор4Наследование
Основы синтаксиса наследования (:)Ключевое слово 'base'Ключевые слова Virtual и OverrideЗапечатанные классыБазовый класс 'object'Итоги — Иерархия сотрудников7Продвинутые возможности
Перегрузка операторовИндексаторы (this[])Переопределение ToString()Методы расширенияПовторение — Пользовательский список10Паттерны проектирования. Часть 1
Введение в паттерны проектированияПотокобезопасный SingletonПаттерн FactoryПаттерн Observer (События)Паттерн Strategy13Финальные испытания
Платформа для онлайн-обученияКомпонент игрового персонажаСервис аренды транспорта2Свойства и статические члены
Автоматически реализуемые свойстваСвойства только для чтения и записиСтатические поля и методыСтатические классыЧлены в виде выражений5Полиморфизм и интерфейсы
Полиморфизм: компиляция vs выполнениеИнтерфейс vs абстрактный классМножественные интерфейсыЯвные интерфейсыUpcasting и DowncastingИтоги: Калькулятор фигур8Продвинутые концепции ООП
Композиция вместо наследованияGenerics (классы и методы)Делегаты и событияАтрибуты и рефлексияIDisposable и оператор usingОсновы Dependency Injection11Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн Компоновщик3Архитектура классов
Экземплярные и статические данныеКлючевые слова 'readonly' и 'const'Опорные поляПовторение — Менеджер банковских счетов6Инкапсуляция
Модификаторы доступаСвойства для инкапсуляцииРеализация скрытия данныхПаттерны неизменяемостиИтоги — Записи студентов9Переменное количество аргументов
Ключевое слово 'params'Необязательные параметрыИменованные аргументыПерегрузка методовПотренируйтесь самостоятельно: Онлайн-компилятор C#