JavaRush /Курси /C# SELF /Шаблон «Видавець-Підписник» та події у C# (

Шаблон «Видавець-Підписник» та події у C# ( event)

C# SELF
Рівень 52 , Лекція 0
Відкрита

1. Вступ

Працюючи з програмами, ви неминуче стикаєтеся із ситуаціями, коли одна частина має «сповістити» інші про те, що відбулося щось важливе. Класичний приклад: користувач клікнув мишею — цю подію слід обробити. У повсякденному житті ми живемо у світі подій: на кухні засвистів чайник — ви почули сигнал і поспішили вимкнути плиту. Кава пролилася на клавіатуру — серце завмерло — і ви кинулися рятувати ноутбук. Програмування підкоряється тим самим законам.

Подія — це механізм, що дозволяє обʼєкту-джерелу (видавцю) сповіщати інші обʼєкти (підписників) про зміни або дії, які відбулися. Це щось на кшталт: «я оголосив — хто почув, той відгукнувся».

У C# події — спеціальна конструкція на основі делегата. Делегат визначає сигнатуру зворотного виклику — що саме і як буде викликано в підписників. Подію оголошують ключовим словом event, а делегата — ключовим словом delegate.

Навіщо потрібні події?

  • Слабке звʼязування: видавець нічого не знає про підписників — лише подає сигнал.
  • Гнучкість архітектури: можна динамічно додавати й прибирати обробники, не змінюючи код видавця.
  • Масштабованість: додали нового підписника — і він одразу почав отримувати сповіщення.

Класичний шаблон «Видавець-Підписник»

Уявімо, що є клас «Пожежна сигналізація» (видавець) і клас «Людина в будівлі» (підписник). Коли спрацьовує сигналізація, вона подає сигнал усім одночасно — байдуже, скільки людей у будівлі й де саме. Це і є шаблон «Видавець-Підписник» (або Observer).

Видавець не знає, скільки й яких підписників існує — він просто сповіщає, а решта самі підписуються на сповіщення або ігнорують їх.

Як це працює в C#?

  • Видавець: визначає подію (event) й дає змогу підписуватися та відписуватися.
  • Підписник: підписується на подію та реалізує обробник (метод-обробник буде викликаний під час настання події).

2. Події на практиці: перший приклад

Від побуту до коду: опишемо найпростішу модель. Припустимо, у нас консольний застосунок, де обʼєкт-таймер щосекунди «тикає», а різні обробники реагують (наприклад, виводять «тік» у консоль або рахують кількість тiків).

Крок 1. Визначаємо делегат і подію


public class SimpleTimer
{
    // Оголошуємо делегата для події
    public delegate void TickEventHandler(object sender, EventArgs e);

    // Подія на основі делегата
    public event TickEventHandler Tick;

    public void Start(int count)
    {
        for (int i = 0; i < count; i++)
        {
            System.Threading.Thread.Sleep(1000); // імітація тіку
            OnTick(); // згенерувати подію
        }
    }

    protected virtual void OnTick()
    {
        // Викликаємо подію, якщо є підписники (Tick != null)
        Tick?.Invoke(this, EventArgs.Empty);
    }
}

Що тут відбувається?

  • Визначено делегат TickEventHandler із класичною сигнатурою object sender, EventArgs e.
  • Подія Tick — точка підписки для обробників.
  • Метод Start імітує «тикання» та періодично викликає OnTick.
  • У OnTick подію викликаємо безпечно: Tick?.Invoke(..., EventArgs.Empty).

Крок 2. Підписка на подію


class Program
{
    static void Main()
    {
        var timer = new SimpleTimer();

        // Підписуємося на подію Tick
        timer.Tick += Timer_Tick;

        timer.Start(3);

        // Можна відписатися, якщо треба
        timer.Tick -= Timer_Tick;
    }

    static void Timer_Tick(object sender, EventArgs e)
    {
        Console.WriteLine("Тік!");
    }
}

Створюємо таймер, підписуємося оператором +=, далі під час кожного тіку викликається обробник. Відписка — оператор -=.

3. Корисні нюанси

Чому події кращі за «жорсткі» виклики?

Якби SimpleTimer у OnTick безпосередньо писав у консоль, клас був би жорстко привʼязаний до конкретної дії. А події «розвʼязують» код: таймер не знає, що саме робитимуть підписники — запускати ракету, логувати у файл чи надсилати електронні листи.

Важлива різниця між подіями та делегатами

  • Делегат — «вказівник» на метод, а подія — це делегат з обмеженнями доступу.
  • Підписники можуть лише підписуватися/відписуватися; викликати подію ззовні не можна — це може зробити лише сам видавець.
  • Щоб оголосити подію, додайте модифікатор event до типу делегата — компілятор забезпечить коректну модель доступу.

Коротка схема роботи події в C#


+------------------+          +------------------------------+
|                  |          |                              |
|     Видавець     | <------> |         Підписник            |
| (Publisher/Event)|          |      (Subscriber/Handler)    |
|                  |          |                              |
+------------------+          +------------------------------+
         | 1) оголошує подію        | 2) підписується на неї
         | 3) викликає її           | 4) реалізує обробник

Коли використовувати події?

  • Потрібно сповістити невизначену кількість слухачів про те, що сталося.
  • Не хочете жорстко звʼязувати логіку дій усередині класу-джерела.
  • UI, асинхронна взаємодія, системні сповіщення — усе крутиться навколо подій.

Короткі особливості реалізації подій у C#

  • Подію не можна викликати «ззовні»: тільки код видавця має право на Invoke.
  • Підписка/відписка: оператори +=/-=; обробників може бути кілька.
  • Подія — це фактично список делегатів: під час настання події викликаються всі обробники в порядку підписки.
  • Рекомендовані делегати: використовуйте EventHandler і EventHandler<TEventArgs> для сумісності з .NET-екосистемою.

4. Типові помилки новачків

Забувають перевіряти, що є підписники: Tick != null. Краще використовувати безпечний виклик: Tick?.Invoke(...).

Підписуються на подію, але не відписуються, коли обробник уже не потрібен. Це може утримувати обʼєкти в памʼяті та призводити до витоків памʼяті.

Намагаються «викликати» подію ззовні — компілятор не дозволить. Не можна написати щось на кшталт game.GameOver(), якщо це подія, а не метод.

Не дотримуються сигнатури делегата. Для подій використовуйте стандартні EventHandler або EventHandler<TEventArgs> — так код сумісний із рештою бібліотек .NET.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ