Загрузка...

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

Делегаты и события в C# являются ключевыми механизмами для построения гибкой и модульной архитектуры приложений. Делегат — это типобезопасный указатель на метод, который позволяет передавать методы как параметры, хранить их и вызывать динамически во время выполнения. События строятся на основе делегатов и реализуют паттерн "издатель-подписчик", позволяя объектам уведомлять другие объекты о произошедших действиях, при этом минимизируя их взаимозависимость.
Использование делегатов и событий оправдано в системах уведомлений, обработке пользовательского интерфейса, логировании, плагин-системах и асинхронных процессах. Делегаты обеспечивают вызов методов без жесткой привязки к конкретной реализации, а события позволяют нескольким подписчикам реагировать на одно действие издателя.
В этом руководстве читатель научится создавать, подписывать и вызывать делегаты, определять события и подписываться на них, а также интегрировать эти механизмы с принципами ООП, структурами данных и алгоритмами для решения практических задач в C#.

Базовый Пример

text
TEXT Code
using System;

namespace DelegatesAndEventsDemo
{
public delegate void NotificationHandler(string message);

public class Publisher
{
public event NotificationHandler Notify;

public void SendNotification(string message)
{
Notify?.Invoke(message);
}
}

public class Subscriber
{
private string _name;

public Subscriber(string name)
{
_name = name;
}

public void OnNotificationReceived(string message)
{
Console.WriteLine($"{_name} получил сообщение: {message}");
}
}

class Program
{
static void Main(string[] args)
{
Publisher publisher = new Publisher();

Subscriber alice = new Subscriber("Алиса");
Subscriber bob = new Subscriber("Боб");

publisher.Notify += alice.OnNotificationReceived;
publisher.Notify += bob.OnNotificationReceived;

publisher.SendNotification("Привет всем подписчикам!");

publisher.Notify -= bob.OnNotificationReceived;
publisher.SendNotification("Второе уведомление");

Console.ReadLine();
}
}

В этом примере делегат NotificationHandler определяет сигнатуру методов для уведомлений, обеспечивая типобезопасность. Класс Publisher содержит событие Notify, которое вызывается через оператор ?.Invoke(), чтобы гарантировать безопасный вызов только при наличии подписчиков.
Класс Subscriber реализует метод OnNotificationReceived, соответствующий сигнатуре делегата. В основном методе создаются объекты Publisher и Subscriber, подписка и отписка на события демонстрируют управление жизненным циклом подписчиков. Такой подход реализует слабую связанность компонентов, безопасный вызов событий и предотвращает утечки памяти.

Практический Пример

text
TEXT Code
using System;
using System.Collections.Generic;

namespace DelegatesAndEventsAdvanced
{
public delegate void DataProcessedHandler(int result);

public class DataProcessor
{
public event DataProcessedHandler DataProcessed;

public void ProcessData(List<int> data)
{
int sum = 0;
foreach (var num in data)
{
if (num < 0)
{
Console.WriteLine("Пропущено отрицательное значение: " + num);
continue;
}
sum += num;
}

DataProcessed?.Invoke(sum);
}
}

public class Logger
{
public void LogResult(int result)
{
Console.WriteLine($"Результат записан: {result}");
}
}

public class Notifier
{
public void SendAlert(int result)
{
if (result > 50)
Console.WriteLine("Внимание! Результат превышает порог: " + result);
}
}

class Program
{
static void Main(string[] args)
{
DataProcessor processor = new DataProcessor();
Logger logger = new Logger();
Notifier notifier = new Notifier();

processor.DataProcessed += logger.LogResult;
processor.DataProcessed += notifier.SendAlert;

List<int> sampleData = new List<int> { 10, 20, 30, -5 };
processor.ProcessData(sampleData);

Console.ReadLine();
}
}

В этом примере DataProcessor суммирует положительные элементы списка и игнорирует отрицательные значения. Событие DataProcessed позволяет классам Logger и Notifier реагировать на результат, не зная деталей вычислений. Logger записывает результат, а Notifier отправляет предупреждение при превышении порога.
Пример демонстрирует паттерн Observer, соблюдение принципа единой ответственности (SRP), безопасный вызов событий, слабую связанность компонентов и обработку исключений.

C# Best Practices и типичные ошибки:

  • Использовать типобезопасные делегаты и инкапсулированные события.
  • Вызывать событие через ?.Invoke() для предотвращения NullReferenceException.
  • Удалять подписчиков, когда они больше не нужны, чтобы избежать утечек памяти.
  • Внимательно использовать анонимные делегаты для подписки.
  • Не выполнять долгие операции на основном потоке при частых событиях.
  • Минимизировать выделение объектов в высокочастотных событиях.
  • Проверять корректность данных перед вызовом событий.

📊 Справочная Таблица

C# Element/Concept Description Usage Example
Delegate Типобезопасный указатель на метод public delegate void MyDelegate(int x);
Event Событие на основе делегата public event MyDelegate MyEvent;
Subscription Подписка на событие myPublisher.MyEvent += mySubscriber.MyMethod;
Unsubscription Отписка от события myPublisher.MyEvent -= mySubscriber.MyMethod;
Conditional Invocation Безопасный вызов события MyEvent?.Invoke(42);
Anonymous Delegate Определение делегата inline myPublisher.MyEvent += (x) => Console.WriteLine(x);

Резюме и следующие шаги:
Делегаты и события обеспечивают гибкую, слабо связанную архитектуру C# приложений. Основные концепции: типобезопасные делегаты, определение событий, безопасная подписка/отписка и применение паттерна Observer.
Для дальнейшего изучения рекомендуется освоить асинхронное программирование, интеграцию делегатов с LINQ и архитектуру на основе событий. Практические применения включают системы уведомлений, фреймворки плагинов и обработку данных в реальном времени. Microsoft Docs и open-source проекты предоставляют дополнительные ресурсы.

🧠 Проверьте Свои Знания

Готов к Началу

Test Your Knowledge

Test your understanding of this topic with practical questions.

4
Вопросы
🎯
70%
Для Прохождения
♾️
Время
🔄
Попытки

📝 Инструкции

  • Внимательно прочитайте каждый вопрос
  • Выберите лучший ответ на каждый вопрос
  • Вы можете пересдавать тест столько раз, сколько захотите
  • Ваш прогресс будет показан вверху