События и делегаты. Понятие события. Взаимодействие между событиями

События и делегаты. Понятие события. Взаимодействие между событиями


Содержание


1. Что такое событие в C#? Какая целесообразность использования событий в языке C#?

Событие – это автоматическое сообщение о том, что в программе состоялось некоторое действие.

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

События работают в объединении с делегатами. Такое объединение позволяет формировать списки (цепочки) обработчиков события (методов), которые должны вызваться при вызове (запуске, генерировании) данного события. Такой подход есть эффективным при написании больших программных систем, поскольку он позволяет упорядочить большой сложный программный код в котором очень легко допустить логическую ошибку.

На рисунке 1 схематично отображена работа события MyEvent.

Рис. 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();
        }
    }
}

 


Связанные темы