1. Синтаксис підписки на подію
Уявіть застосунок для керування завданнями: коли завдання завершується, спрацьовує подія, а обробник може, наприклад, надіслати сповіщення, оновити UI або записати інформацію у журнал. C# робить підписку на події зручною та безпечною: ви явно вказуєте, який обробник реагує на яку подію.
Підписка на подію в C# — це майже як додати себе до списку гостей на вечірку:
publisher.MyEvent += HandlerMethod;
Тут publisher — об’єкт, який оголосив подію MyEvent, а HandlerMethod — метод, який буде викликано, коли ця подія спрацює.
Подивімося на це в контексті мінімального прикладу. Припустімо, у нас є застосунок для підрахунку кліків:
public class Clicker
{
public event Action Clicked;
public void Click()
{
// Щось було натиснуто!
Clicked?.Invoke();
}
}
Ось наш «видавець» події Clicked. Тепер підпишімо обробник:
Clicker clicker = new Clicker();
void OnClicked()
{
Console.WriteLine("Кнопку натиснули!");
}
clicker.Clicked += OnClicked;
// Десь у коді
clicker.Click();
// → "Кнопку натиснули!"
Як це працює? На подію «натискання» ми додали наш метод OnClicked, і він викликатиметься щоразу, коли відбувається клік.
2. Обробники подій: які бувають і як їх оголошувати
Обробник події — це метод, який викликається, коли спрацьовує подія. Його сигнатура має збігатися з типом делегата події. Наприклад, якщо подію оголошено як public event Action Clicked;, то обробник має бути методом без параметрів і без значення, що повертається.
Обробник для Action
void OnClicked()
{
Console.WriteLine("Подія сталася (Action)!");
}
Обробник для стандартного EventHandler
Коли ви використовуєте класичний підхід з EventHandler, обробник приймає два параметри: відправника (object sender) і дані події (EventArgs e):
public class Alarm
{
public event EventHandler AlarmRaised;
public void RaiseAlarm()
{
AlarmRaised?.Invoke(this, EventArgs.Empty);
}
}
Alarm alarm = new Alarm();
void AlarmHandler(object sender, EventArgs e)
{
Console.WriteLine("Спрацювала тривога!");
}
alarm.AlarmRaised += AlarmHandler;
alarm.RaiseAlarm();
Анонімні методи і лямбда-вирази
C# дає змогу використовувати анонімні функції та лямбда-вирази безпосередньо під час підписки:
clicker.Clicked += () => Console.WriteLine("Ще один клік!");
Або трохи складніше, якщо подія має аргументи:
alarm.AlarmRaised += (sender, e) =>
{
Console.WriteLine($"Тривогу згенерував: {sender}");
};
4. Корисні нюанси
Підписка і відписка: важливі нюанси
Реальне життя — це коли гостей на вечірці стає забагато або хтось хоче піти додому. З делегатами так само: обробник можна додати (підписатися) і прибрати (відписатися):
// Підписка
publisher.MyEvent += MyHandler;
// Відписка (коли обробник більше не потрібен)
publisher.MyEvent -= MyHandler;
Чому це важливо? Якщо не відписуватися, особливо у великих застосунках, обробники можуть залишитися «висячими» і не дати збирачу сміття очистити об’єкти — звідси витоки пам’яті.
Кілька обробників
На одну подію можна підписати скільки завгодно обробників. Вони викликатимуться по черзі в тому порядку, у якому були додані.
clicker.Clicked += () => Console.WriteLine("Перший обробник!");
clicker.Clicked += () => Console.WriteLine("Другий обробник!");
clicker.Click();
// → Перший обробник!
// → Другий обробник!
Ви навіть можете підписувати й відписувати обробники прямо «на льоту» — це гнучко й зручно.
Чому це справді важливо: реальні сценарії
- UI (Windows Forms, WPF, WinUI, MAUI): натискання кнопки, зміна тексту в полі — усе це події.
- FileSystemWatcher: сповіщення про появу нових файлів у теці.
- Асинхронні операції: завершення завантаження файлу, прогрес виконання завдань.
- Система плагінів: окремі модулі підписуються на події основного застосунку.
Подійна модель дає змогу будувати розширювані архітектури: ви можете додавати нові модулі та підписуватися на вже наявні події без змін базового коду.
Приклади підписки
| Сигнатура події | Приклад підписки | Приклад обробника |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
5. Типові помилки та підводні камені
Помилка №1: невідповідність сигнатури обробника.
Якщо подію оголошено як event Action<int>, а ви намагаєтеся підписати метод без параметрів, компілятор видасть помилку. Завжди перевіряйте, що метод відповідає потрібній сигнатурі події.
Помилка №2: захоплення змінних у лямбдах.
Під час підписки лямбда-вираз може захоплювати змінні із зовнішньої області видимості (див. тему «Замикання»). Якщо після підписки значення змінної зміниться, обробник бачитиме вже нове значення, що може призвести до неочікуваних результатів.
Помилка №3: підписка на неініціалізовану подію.
Якщо ви підписуєтеся на подію до того, як створено й ініціалізовано об’єкт, що містить цю подію, ризикуєте отримати NullReferenceException. Перед підпискою переконайтеся, що об’єкт готовий до використання.
Помилка №4: множинна підписка одного обробника.
Якщо один і той самий обробник підписано на подію кілька разів, його викличуть стільки ж разів. Це не помилка, а особливість механізму подій, але часто стає неприємним сюрпризом.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ