Menu
Coddy logo textTech

Делегаты и события

Часть раздела Объектно-ориентированное программирование путешествия по 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. События имеют фундаментальное значение для приложений с графическим интерфейсом, игровых движков и любых систем, в которых объектам нужно реагировать на изменения без жёсткой связанности.

challenge icon

Задание

Легко

Давайте создадим систему мониторинга температуры, которая использует делегаты и события, чтобы уведомлять subscribers всякий раз, когда температура изменяется. Это классический пример шаблона publish-subscribe, который event позволяет реализовать особенно элегантно.

Вы организуете код в двух файлах:

  • TemperatureMonitor.cs: создайте TemperatureMonitor class в namespace Monitoring, который выступает в роли издателя. Ваш monitor должен:
    • иметь private field для хранения текущей температуры
    • объявлять event с использованием Action<int> под названием TemperatureChanged, который срабатывает всякий раз, когда температура обновляется
    • иметь свойство Temperature (int), setter которого обновляет private field и вызывает event с новым значением температуры
    Не забудьте использовать шаблон ?.Invoke(), чтобы безопасно вызывать event только при наличии subscribers.
  • Program.cs: в основном файле создайте TemperatureMonitor и подпишите два разных handler на его event TemperatureChanged:
    • один handler, который выводит: Alert: Temperature is now {value} degrees
    • другой handler, который выводит: Logged: {value}
    После подписки обоих handler дважды установите температуру, используя значения из input. Каждый раз при установке температуры оба 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
    }
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор C#