Урок 15. События C#


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

Что такое событие?

В первых четырнадцати частях учебника по объектно-ориентированному программированию на языке C# мы описали создание классов с методами, свойствами и другими структурами. Каждый из них похож, потому что он позволяет дать прямую команду классу или объекту в потоке программы. Это похоже на ранние языки программирования, которые просто обрабатывали последовательность команд и циклов до полного завершения.

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

Ключевым преимуществом использования событий является то, что классу, публикующему событие, не нужно знать подробности о подписчиках во время разработки. Поскольку события основаны на делегатах, подписчики добавляются только тогда, когда программа запущена.

Создание события

Процесс создания обработчика событий в классе состоит из трех частей. Во-первых, требуется делегат. Делегат используется для хранения сведений о подписчиках на мероприятие. Во-вторых, создается общественное мероприятие. Это событие отображается внешне для класса и используется для создания подписок на события. Это также элемент, который виден с помощью функции Intellisense Visual Studio. Наконец, внутри класса создается метод. Метод содержит код, который запускает событие. Каждый из этих трех элементов описан в следующих разделах.

Чтобы привести пример использования событий, мы создадим новую программу, содержащую класс, представляющий автомобиль. Когда скорость автомобиля превысит предел безопасности, произойдет событие. Для начала создайте новое консольное приложение с именем "EventDemo". Добавьте файл класса с именем "Car" в проект и добавьте следующий код в класс, чтобы создать свойство скорости только для чтения и метод ускорения. Переменная _safetySpeed содержит максимальную разрешенную скорость и будет использоваться позже.

public class Car
{
    private int _speed = 0;
    private int _safetySpeed = 70;
 
    public int Speed
    {
        get
        {
            return _speed;
        }
    }
 
    public void Accelerate(int mph)
    {
        _speed += mph;
    }
}
Создание делегата

Делегат для события объявляется с использованием стандартного синтаксиса для любого делегата. Однако он должен быть объявлен с типом данных void, поскольку события являются многоадресными. Это означает, что к одному событию можно подключить несколько подписчиков, каждый из которых будет уведомлен о событии по очереди.

Подпись делегата важна, так как это та подпись, которую увидят подписчики. Чтобы соответствовать ожидаемым стандартам, подпись должна содержать два параметра. Первым параметром должен быть объект с именем "источник". Это будет содержать ссылку на объект, вызвавший событие. Второй параметр должен иметь тип EventArgs и должен быть назван "e". EventArgs-это специальный класс, производный от которого можно создавать пользовательские классы для передачи информации при возникновении события. Это описано далее в этой статье.

В этом примере мы добавим событие к классу автомобилей, которое возникает, когда автомобиль превышает предельную скорость безопасности. Чтобы создать делегат для этого события, добавьте следующий код в пространство имен (вне определения класса).

public delegate void SpeedLimitExceededEventHandler(object source, EventArgs e);
Объявление события

Событие является публично видимым элементом класса. Он может быть подписан на другие классы, которые хотят реагировать на запускаемое событие. Событие объявляется путем префикса его имени с ключевым словом event и именем делегата, на котором будет основано событие. Чтобы создать событие для класса Car, добавьте в него следующее:

public event SpeedLimitExceededEventHandler SpeedLimitExceeded;
Создание метода события

Метод события - это единственный метод, используемый для создания события. Хотя это не является строго необходимым для создания такого метода, он является выгодным, поскольку он упрощает обслуживание кода и позволяет производным классам переопределять функциональность и изменять способ запуска событий.

Чтобы создать метод события для события SpeedLimitExceeded, добавьте следующий код в класс Car.

public virtual void OnSpeedLimitExceeded(EventArgs e)
{
    if (SpeedLimitExceeded != null) SpeedLimitExceeded(this, e);
}
В этом методе можно отметить несколько элементов. Во-первых, метод помечен как виртуальный. Это ключевое слово будет подробно описано далее в учебнике. Это означает, что функциональность этого метода может быть переопределена и изменена классами, производными от класса Car.

Метод называется с использованием имени события и префикса "On". Это простое соглашение, которое разработчики ожидают увидеть. Метод event требует только одного параметра, содержащего аргументы события для вызванного события. Этот параметр содержит всю информацию, которая должна быть передана подписчикам события.

В блоке кода метода оператор if проверяет, установлено ли для события значение null. Этот условный оператор проверяет, что существует по крайней мере один подписчик на событие, как если бы подписчиков не было, запуск события вызвал бы исключение. Если тест пройден, то событие вызывается так, как если бы это был метод, передающий ссылку на текущий объект и аргументы события, которые были переданы в параметре.

Метод вызова события

Для завершения примера класса автомобиля, метод события должен быть вызван, когда автомобиль разгоняется выше скорости безопасности. Измените метод ускорения следующим образом, чтобы событие возникало, когда скорость автомобиля изменяется снизу или равна скорости безопасности, чтобы она была выше.

public void Accelerate(int mph)
{
    int oldSpeed = _speed;
    _speed += mph;
 
    if (oldSpeed <= _safetySpeed && _speed > _safetySpeed)
        OnSpeedLimitExceeded(new EventArgs());
}
Подписка на событие

Чтобы подписаться на событие, к нему необходимо добавить ссылку на метод. Это достигается аналогично добавлению ссылок на делегат многоадресной рассылки. Каждый метод, который должен быть вызван при возникновении события, добавляется к событию с помощью оператора сложного назначения addition (+=). Сигнатура добавленных методов должна совпадать с сигнатурой делегата, на котором основано событие.

Добавление метода CarSpeedLimitExceeded

В примере автомобиля мы добавим новый метод, который фиксирует событие, которое происходит, когда автомобиль превышает безопасную скорость. Этот метод, названный CarSpeedLimitExceeded, добавляется в класс программы консольного приложения. Он будет извлекать скорость из исходного объекта и выводить предупреждающее сообщение. Два параметра метода соответствуют делегату события и получат объект Car, вызывающий событие и связанные с ним аргументы события. Добавьте в класс Program следующий метод:

static void CarSpeedLimitExceeded(object source, EventArgs e)
{
    Car car = (Car)source;
    Console.WriteLine("Speed limit exceeded ({0}mph)", car.Speed);
}
Подписка на событие SpeedLimitExceeded

Теперь, когда событие было создано и существует метод, который будет реагировать на событие, мы создадим объект Car и подпишемся на его событие. Добавьте следующий код к основному методу программы:

Car car = new Car();
car.SpeedLimitExceeded += new SpeedLimitExceededEventHandler(CarSpeedLimitExceeded);
Первая строка кода выше создает новый объект Car. Вторая строка, использующая нотацию составного присвоения для связи метода CarSpeedExceeded с событием SpeedLimitExceeded объекта. Теперь, когда событие будет запущено, связанный метод будет выполнен.

Чтобы протестировать событие, измените метод Main следующим образом и выполните программу:

static void Main(string[] args)
{
    Car car = new Car();
    car.SpeedLimitExceeded +=
        new SpeedLimitExceededEventHandler(CarSpeedLimitExceeded);
 
    for (int i = 0; i < 3; i++)
    {
        car.Accelerate(30);
        Console.WriteLine("Speed: {0}mph", car.Speed);
    }
}
 
/* 
 
Speed: 30mph
Speed: 60mph
Speed limit exceeded (90mph)
Speed: 90mph
 
*/
Удаление подписок на события

Как и в случае с делегатами, важно освободить ссылки на события, когда они больше не требуются. Для завершения подписки используйте оператор соединения вычитания ( -=). Например, чтобы отменить подписку и освободить ссылку CarSpeedLimitExceeded, можно использовать следующий код:

car.SpeedLimitExceeded -= new SpeedLimitExceededEventHandler(CarSpeedLimitExceeded);
Аргумент события

Ранее в этой статье я упоминал об использовании аргументов событий. Они содержат дополнительную информацию, которая передается при возникновении события. До сих пор мы использовали базовый объект EventArgs для представления этих аргументов событий. Класс EventArgs не содержит свойств, которые делают его полезным сам по себе. Чтобы предоставить дополнительную информацию, нам нужно создать новый класс, производный от EventArgs, с дополнительными свойствами, которые мы хотим передать.

Создание пользовательского класса EventArgs

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

Для начала создайте новый файл класса, содержащий класс SpeedEventArgs. Чтобы указать, что класс является производным от EventArgs, имя базового класса добавляется после имени нового класса с двумя разделенными двоеточием (:). Новый класс требует одного свойства, которое удерживает избыточную скорость для автомобилей, движущихся быстрее, чем их безопасная скорость. Таким образом, окончательный код для класса является:

public class SpeedEventArgs : EventArgs
{
    private int _excessSpeed;
 
    public int ExcessSpeed
    {
        get
        {
            return _excessSpeed;
        }
        set
        {
            _excessSpeed = value;
        }
    }
}
Чтобы использовать аргументы события с нашим существующим событием, необходимо внести несколько изменений. Во-первых, делегат для события должен быть обновлен, чтобы заменить EventArgs на SpeedEventArgs следующим образом:

public delegate void SpeedLimitExceededEventHandler(object source, SpeedEventArgs e);
Сигнатура метода OnSpeedLimitExceeded должна быть обновлена для использования нового типа:

public virtual void OnSpeedLimitExceeded(SpeedEventArgs e)
Затем вызов OnSpeedLimitExceeded должен быть изменен. Метод ускорения теперь будет вычислять разницу между текущей скоростью автомобиля и скоростью безопасности. Это будет присвоено свойству аргумента события, чтобы его можно было передать подписывающимся методам. Обновленный метод ускорения выглядит следующим образом:

public void Accelerate(int mph)
{
    int oldSpeed = _speed;
    _speed += mph;
 
    if (oldSpeed <= _safetySpeed && _speed > _safetySpeed)
    {
        SpeedEventArgs e = new SpeedEventArgs();
        e.ExcessSpeed = _speed - _safetySpeed;
        OnSpeedLimitExceeded(e);
    }
}
Наконец, сигнатура подписывающего метода в классе программы должна быть обновлена, чтобы принять новые аргументы события и реагировать соответствующим образом:

static void CarSpeedLimitExceeded(object source, SpeedEventArgs e)
{
    Car car = (Car)source;
    Console.WriteLine("Speed limit exceeded by {0}mph", e.ExcessSpeed);
}
Эти изменения означают, что превышение скорости теперь сообщается самим событием. Поскольку программа не обязана знать, каков предел безопасности автомобиля, это является хорошим примером инкапсуляции. Выполните программу, чтобы увидеть новые результаты.
Автор этого материала - я - Пахолков Юрий. Я оказываю услуги по написанию программ на языках Java, C++, C# (а также консультирую по ним) и созданию сайтов. Работаю с сайтами на CMS OpenCart, WordPress, ModX и самописными. Кроме этого, работаю напрямую с JavaScript, PHP, CSS, HTML - то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.

тегистатьи IT, си шарп, ООП




Отправляя сообщение я подтверждаю, что ознакомлен и согласен с политикой конфиденциальности данного сайта.




2D платформер на Unity: управляем котом с помощью джойстика
Бизнес и PHP
C#: NaN и IsNaN