Шаг 184.
Язык программирования C#. Начала.
Делегаты и события. Знакомство с событиями (продолжение)

    На этом шаге мы рассмотрим пример использования событий.

    Небольшой пример, в котором используются события, представлен ниже.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace pr184_1
{
    // Объявление делегата:
    delegate void MyDelegate(string txt);

    // Класс с событием: 
    class MyClass {
        // Объявление события: 
        public event MyDelegate MyEvent;
        // Метод для генерирования события: 
        public void RaiseMyEvent(string txt) {
            // Если для события предусмотрены обработчики: 
            if (MyEvent != null) {
                // Генерирование события:
                MyEvent(txt);
            }
        }
    }

    // Класс: 
    class Alpha {
        // Текстовое поле: 
        public string name;
        // Конструктор: 
        public Alpha(string txt) { 
            name = txt;
        }
        // Метод отображает сообщение: 
        public void show(string msg) {
            Console.WriteLine("Объект " + name + ":");
            Console.WriteLine(msg);
        }
    }

    // Класс с главным методом: 
    class Program
    {
        // Главный метод:
        static void Main()
        {
            // Создание объекта с событием:
            MyClass obj = new MyClass();
            // Создание объектов:
            Alpha A = new Alpha("A");
            Alpha B = new Alpha("B");
            // Попытка сгенерировать событие: 
            obj.RaiseMyEvent("1-e событие");
            // Добавление обработчика для события: 
            obj.MyEvent += A.show;
            // Генерирование события: 
            obj.RaiseMyEvent("2-e событие");
            Console.WriteLine();
            // Добавление обработчика для события: 
            obj.MyEvent += B.show;
            // Генерирование события: 
            obj.RaiseMyEvent("3-е событие");
            Console.WriteLine();
            // Удаление метода из списка обработчиков события: 
            obj.MyEvent -= A.show;
            // Генерирование события: 
            obj.RaiseMyEvent("4-e событие");
            Console.WriteLine();
            // Удаление методов из списка обработчиков события:
            obj.MyEvent -= A.show;
            obj.MyEvent -= B.show;
            // Попытка сгенерировать событие: 
            obj.RaiseMyEvent("5-e событие");
            // Создание экземпляра делегата:
            MyDelegate md = A.show;
            // Добавление метода в список вызовов 
            // экземпляра делегата: 
            md += B.show;
            // Добавление экземпляра делегата в список 
            // обработчиков события: 
            obj.MyEvent += md;
            // Генерирование события: 
            obj.RaiseMyEvent("6-е событие");
            // Задержка:
            Console.ReadLine();
        }
    }
}
Архив проекта можно взять здесь.

    Результат выполнения программы представлен ниже:


Рис.1. Результат выполнения программы

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

    Класс MyClass содержит объявление события MyEvent. Оно описано с ключевым словом event, типом события указан делегат MyDelegate. Это означает, что для обработки события могут использоваться методы, сигнатура которых соответствует характеристикам делегата MyDelegate: у метода должен быть текстовый аргумент, и метод не должен возвращать результат. Еще в классе MyClass описан открытый метод RaiseMyEvent() с текстовым аргументом (обозначен как txt). Метод предназначен для генерирования события MyEvent. В теле метода с помощью условной конструкции проверяется условие MyEvent!=null, и если оно истинно, то командой MyEvent(txt) генерируется событие MyEvent. После генерирования события командой MyEvent(txt) автоматически вызываются все методы, зарегистрированные в качестве обработчиков данного события. Каждому из методов аргументом передается текстовое значение txt.


Зачем нам нужен метод RaiseMyEvent()? Понятно, что событие генерируется при выполнении команды MyEvent(txt). Но дело в том, что вызвать (сгенерировать) событие командой MyEvent(txt) мы можем только в теле класса. Поэтому мы описываем открытый метод RaiseMyEvent(), который можно вызвать через ссылку на объект класса MyClass. А при вызове метода RaiseMyEvent() уже будет сгенерировано событие MyEvent (для объекта, из которого вызывается метод RaiseMyEvent()).

    Если при генерировании события окажется, что список обработчиков для этого события пустой, то возникнет ошибка. Поэтому перед генерированием события командой MyEvent(txt) в теле метода RaiseMyEvent() сначала проверяется условие MyEvent!=null. Оно истинно, если список обработчиков для события не пустой. Только в этом случае генерируется событие.


    В классе Alpha есть открытое текстовое поле name. Конструктор с одним текстовым аргументом позволяет присвоить значение полю при создании объекта. Также в классе есть метод show(). Метод не возвращает результат и имеет один текстовый аргумент (обозначен как msg). Таким образом, метод show() полностью соответствует характеристикам делегата MyDelegate и может быть использован как обработчик события MyEvent. В теле метода show() командой

  Console.WriteLine("Объект " + name + ":");
отображается значение поля name объекта, из которого вызывается метод, а затем командой
  Console.WriteLine(msg);
отображается значение аргумента, переданного методу.

    В главном методе программы мы создаем объект obj класса MyClass, а также объекты A и B класса Alpha.

    Сначала командой

  obj.RaiseMyEvent("1-e событие");
мы пытаемся сгенерировать событие MyEvent для объекта obj. Но на момент выполнения данной команды список обработчиков для события MyEvent объекта obj пуст, поэтому ничего не происходит. Затем командой
  obj.MyEvent += A.show;
в качестве обработчика события регистрируется метод show() объекта A. Поэтому после генерирования события командой
  obj.RaiseMyEvent("2-e событие");
из объекта A вызывается метод show() с аргументом "2-е событие".

    На следующем этапе командой

  obj.MyEvent += B.show;
для события обработчиком дополнительно регистрируется еще и метод show() объекта B. Поэтому при генерировании события командой
  obj.RaiseMyEvent("3-е событие");
из объектов A и B с аргументом "3-е событие" последовательно вызывается метод show().

    Командой

  obj.MyEvent -= A.show;
метод show() объекта A удаляется из списка обработчиков для события MyEvent объекта obj (у события остается один обработчик - метод show() объекта B). При генерировании события командой
  obj.RaiseMyEvent("4-e событие");
с аргументом "4-е событие" вызывается метод show() объекта B. Далее командами
  obj.MyEvent -= A.show;
и
  obj.MyEvent -= B.show;
из списка обработчиков события удаляются методы show() объектов A и B. Причем на момент выполнения команды
  obj.MyEvent -= A.show;
метода show() объекта A в списке обработчиков нет, так что здесь имеет место попытка удалить отсутствующий в списке метод. Но, как видим, к ошибке это не приводит. А общий итог такой, что список обработчиков события MyEvent объекта obj становится пустым. Как следствие, при выполнении команды
  obj.RaiseMyEvent("5-e событие");
ничего не происходит (сообщения в консольном окне не появляются).

    Как отмечалось ранее, в список обработчиков могут добавляться экземпляры делегатов или методы. Методы мы уже добавляли. На финальном этапе прибегнем к помощи экземпляров делегата MyDelegate. Командой

  MyDelegate md = A.show;
создается экземпляр md, и в список вызовов делегата добавляется ссылка на метод show() объекта A. Командой
  md += B.show;
туда же добавляется ссылка на метод show() объекта B. А затем командой
  obj.MyEvent += md;
в список обработчиков события MyEvent объекта obj добавляется делегат md. При генерировании события командой
  obj.RaiseMyEvent("6-е событие");
вызывается экземпляр делегата md с аргументом "6-е событие". Вызов этого экземпляра означает вызов метода show() из объектов A и B.

    На следующем шаге мы закончим изучение этого вопроса.




Предыдущий шаг Содержание Следующий шаг