Події і делегати. Поняття події. Взаємодія між подіями

Події і делегати. Поняття події. Взаємодія між подіями


Зміст



1. Що таке подія в C#? Яка доцільність використання подій в мові C#?

Подія – це автоматичне повідомлення про те, що в програмі відбулась деяка дія.

На виконання тієї чи іншої події програма має відповідно реагувати. Реакція на подію здійснюється з допомогою так званих обробників події. Обробник події – це звичайний метод, що виконує деякі дії в програмі, у випадку коли відбулась (згенерувалась) подія.

Події працюють в поєднанні з делегатами. Таке поєднання дозволяє формувати списки (ланцюжки) обробників події (методів), які мають викликатись при виклику (запуску, генеруванні) даної події. Такий підхід є ефективним при написанні великих програмних систем, оскільки він дозволяє впорядкувати великий складний програмний код в якому дуже легко допустити логічну помилку.

На рисунку 1 схематично відображено роботу події MyEvent.

C# ланцюг метод подія рисунок

Рис. 1. Виклик ланцюга методів для події MyEvent

 

2. Яка загальна форма оголошення події? Ключове слово event. Приклад

Оголошення події здійснюється на основі раніше оголошеного типу делегату. Загальна форма оголошення події:

event делегат_події ім’я_події;

де

  • делегат_події – ім’я типу делегату, що використовується для підтримки події;
  • ім’я_події – конкретне ім’я об’єкту (змінної) типу “подія”.

Приклад. Оголошення події з іменем MyEvent на основі типу делегату MyDelType.

event MyDelType MyEvent;

Біль детально про оголошення типу делегату та оголошення делегату описано в темі:

 

3. Які вимоги до делегату, що буде використовуватись для обробки події?

Для того, щоб можна було обробляти (запускати) списки обробників події, делегат не повинен повертати значення. Тобто, делегат має повертати тип void.

Якщо делегат повертає значення, то викликається останній метод (обробник) зі сформованого списку методів.

 

4. Які вимоги ставляться до методу, що має бути обробником деякої події?

Основна вимога – метод має мати таку саму сигнатуру як і делегат, на основі якого оголошена подія.

Якщо потрібно організувати список методів, що викликаються при виклику події, тоді тип делегату і метод повинні повертати значення void (нічого не повертати). Якщо є список методів що повертають значення, то при запуску події з цього списку викликається останній метод.

 

5. Як здійснюється реєстрація методу в події? Приклад

Щоб зареєструвати метод для обробки даної події потрібно використати оператори ‘=’ або ‘+=”.

Приклад.

Нехай в тілі деякого класу оголошено:

  • подію з іменем MyEvent;
  • метод обробки події (обробник події) MyMethod1();
  • обробник події MyMethod2();
  • обробник події MyMethod3().

Методи мають таку саму сигнатуру, як і тип делегату, на основі якого оголошена подія MyEvent. Щоб зареєструвати методи MyMethod1(), MyMethod2(), MyMethod3() для події MyEvent потрібно написати такий програмний код:

...

// реєстрація методів для події MyEvent
MyEvent = MyMethod1(); // MyEvent => MyMethod1

MyEvent += MyMethod2(); // MyEvent => MyMethod1 -> MyMethod2

MyEvent += MyMethod3(); // MyEvent => MyMethod1 -> MyMethod2 -> MyMethod3

...

У цьому випадку утвориться список (ланцюжок) з трьох методів. Методи в списку йдуть в тій послідовності, в якій вони були зареєстровані (додані) в події.

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

 

6. Яка послідовність кроків у програмі для організації списку обробників подій, що будуть викликатись при виклику (запуску) події

Щоб організувати список обробників події (методів) потрібно виконати таку послідовність кроків:

  1. Оголосити тип делегату в класі.
  2. Оголосити подію в даному класі або створити інший клас, що містить оголошення події.
  3. В деякому методі (програмному коді) створити список обробників (методів), які будуть викликатись при виклику даної події. Це здійснюється з допомогою операторів ‘=’ та ‘+=’. Створення списку означає реєстрацію обробників для даної події.
  4. Викликати подію (запустити на виконання) з цього методу.

 

7. Приклад використання події для випадку коли методи і подія оголошені в одному класі
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace TrainEvents03
{
    public partial class Form1 : Form
    {
        // 1. Оголосити тип делегату
        delegate void CalcFigure(double R);

        // 2. Оголосити подію з іменем ECF
        event CalcFigure ECF;

        // 3. Методи обробки події - розміщуються в цьому ж класі
        //   Методи мають таку саму сигнатуру як тип делегату CalcFigure
        // Довжина кола на основі радіусу R
        void GetLength(double R)
        {
            double res;
            const double Pi = 3.1415;
            res = 2 * Pi * R;
            label1.Text = "Довжина кола = " + res.ToString(); // вивести на форму
        }

        // Площа круга
        void GetArea(double R)
        {
            double res;
            const double Pi = 3.1415;
            res = Pi * R * R;
            label2.Text = "Площа круга = " + res.ToString(); // результат - на форму
        }

        // Об'єм кулі
        void GetVolume(double R)
        {
            double res;
            const double Pi = 3.1415;
            res = 4.0 / 3.0 * Pi * R * R * R;
            label3.Text = "Об\'єм кулі = " + res.ToString();
        }

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            // 4. Демонстрація роботи з методами з допомогою події
            // 4.1. Створити ланцюг методів, які будуть викликатись з події ECF
            ECF = GetLength; // ECF => GetLength()
            ECF += GetArea; // ECF => GetLength() -> GetArea()
            ECF += GetVolume; // ECF => GetLength() -> GetArea() -> GetVolume()

            // 4.2. Виклик події ECF з параметром 2.0
            ECF(2.0); // викликаються послідовно три методи GetLength(), GetArea(), GetVolume()

            // Виклик події для параметру 3.5
            ECF(3.5);
        }
    }
}

 

8. Приклад використання події, яка оголошена в окремому класі. Обробники події оголошені в іншому класі

Нехай в модулі CalcFigures.cs оголошено клас, що містить такий опис

namespace TrainEvents05
{
    class CalcFigures
    {
        // клас, що містить методи обробки події
        public void GetLength(double r, ref double L)
        {
            L = (double)(2 * 3.1415 * r);
        }

        public void GetArea(double r, ref double S)
        {
            S = (double)(3.1415 * r * r);
        }
    }
}

В іншому модулі Form1.cs оголошено клас, що демонструє використання методів з класу CalcFigures. Лістинг модуля Form1.cs наступний:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace TrainEvents05
{
    public partial class Form1 : Form
    {
        // оголошення типу делегату
        delegate void CalcFig(double r, ref double l);

        // оголошення події на основі типу CalcFig
        event CalcFig EvCF;

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            // Демонстрація використання події EvCF
            CalcFigures CF = new CalcFigures(); // CF - екземпляр об'єкту класу
            double len, r;
            r = 1.0;
            len = 0;
            EvCF = CF.GetLength;

            // запустити подію
            EvCF(r, ref len); // len = 6.238 - довжина кола

            // видалити зі списку подію
            EvCF -= CF.GetLength;

            // додати до списку інший метод
            EvCF += CF.GetArea;

            // запустити подію
            EvCF(r, ref len); // len = 3.1415 - площа круга
            label1.Text = len.ToString();
        }
    }
}

 


Зв’язані теми