Шаг 200.
Язык программирования C#. Начала.
Перечисления и структуры. Структуры и события

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

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

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

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

    // Первая структура: 
    struct Alpha {
        // Открытое текстовое поле: 
        public string name;
        // Закрытое поле, являющееся ссылкой 
        // на экземпляр делегата: 
        private MyDelegate myevent;
        // Объявление события: 
        public event MyDelegate MyEvent {
            // Аксессор для добавления ссылки на метод
            // в список обработчиков события:
            add {
                myevent += value;
            }
            // Аксессор для удаления ссылки на метод 
            // из списка обработчиков события: 
            remove {
                myevent -= value;
            }
        }
        // Конструктор: 
        public Alpha(string t) {
            // Значения полей:
            name = t;
            myevent = null;
        }
        // Метод для генерирования событий: 
        public void RaiseMyEvent() {
            // Если ссылка не пустая, то вызывается 
            // экземпляр делегата: 
            if (myevent != null) 
                myevent("Alpha: " + name);
        }
    }

    // Вторая структура: 
    struct Bravo {
        // Открытое текстовое поле: 
        public string name;
        // Объявление события: 
        public event MyDelegate MyEvent;
        // Метод для генерирования события: 
        public void RaiseMyEvent() {
            // Если список обработчиков не пустой, то 
            // генерируется событие: 
            if (MyEvent != null) 
                MyEvent("Bravo: " + name);
        }
    }

    // Класс с главным методом:
    class Program
    {
        // Статический метод: 
        static void show(string t) {
            Console.WriteLine("Произошло событие");
            Console.WriteLine(t);
            Console.WriteLine("------------------");
        }
        // Главный метод:
        static void Main()
        {
            // Создание экземпляра первой структуры:
            Alpha A = new Alpha("Экземпляр A");
            // Добавление ссылки на метод в список обработчиков 
            // события:
            A.MyEvent += show;
            // Генерирование события:
            A.RaiseMyEvent();
            // Создание экземпляра второй структуры:
            Bravo B = new Bravo();
            // Присваивание значения текстовому полю:
            B.name = "Экземпляр B";
            // Добавление ссылки на метод в список обработчиков 
            // события:
            B.MyEvent += show;
            // Генерирование событие:
            B.RaiseMyEvent();
            // Задержка:
            Console.ReadLine();
        }
    }
}
Архив проекта можно взять здесь.

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


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

    В программе есть объявление делегата MyDelegate, соответствующего методам с текстовым аргументом, которые не возвращают результат. Экземпляры этого делегата мы планируем использовать для обработки событий. Также мы описали две структуры Alpha и Bravo. Структуры функционально схожи, но немного по-разному организованы. В структуре Alpha есть открытое текстовое поле name. Закрытое поле myevent типа MyDelegate является ссылкой на экземпляр делегата. Мы это поле используем при описании аксессоров события MyEvent: при добавлении ссылки на метод в список обработчиков события или удалении ссылки из списка в действительности изменения происходят с полем myevent. В структуре также есть конструктор с текстовым аргументом. Текстовый аргумент определяет значение текстового поля name, а поле myevent в качестве значения получает пустую ссылку. В данном случае это момент не тривиальный, поскольку в конструкторе должны быть присвоены значения всем полям экземпляра структуры.

    Для генерирования события в структуре описан метод RaiseMyEvent(), в теле которого проверяется условие myevent!=null (истинно, если поле myevent содержит ссылки на методы), и при истинном условии командой

  myevent("Alpha: " + name);
вызывается экземпляр делегата, на который ссылается поле myevent.

    Вторая структура называется Bravo. В ней также есть текстовое поле name. Событие MyEvent объявлено без явного описания аксессоров. Конструктора у структуры нет. Метод RaiseMyEvent() генерирует событие MyEvent при условии, что для события зарегистрированы обработчики.


То, что в структуре Bravo не описан конструктор - не случайно. Каждая версия конструктора, которая описывается в структуре, должна присваивать значения полям экземпляра структуры. За событием "спрятано" поле, которое содержит ссылки на экземпляры делегата, которые вызываются для обработки события. В структуре Alpha такое поле было описано явно, и мы ему присваивали значение в конструкторе. В структуре Bravo мы для события не описываем аксессоры и не объявляем поле для записи ссылок на экземпляры делегата. Такое поле создается автоматически, и присвоить ему значение в конструкторе было бы проблематично. Поэтому от конструктора мы отказались.

    Для обработки событий мы описали статический метод show() с текстовым аргументом. Метод при вызове отображает значение своего текстового аргумента вместе с поясняющим текстом.

    В главном методе командой

  Alpha A = new Alpha("Экземпляр A");
мы создаем экземпляр первой структуры. Командой
  A.MyEvent += show; 
добавляем ссылку на метод show() в список обработчиков события MyEvent экземпляра A. Командой
  A.RaiseMyEvent();
событие генерируется. Аналогичные операции выполняются с экземпляром B структуры Bravo. Заслуживает внимания лишь команда
  Bravo B = new Bravo();          , 
которой создается экземпляр структуры. Конструктор в структуре Bravo мы не описывали, поэтому теоретически вариантов было два: просто объявить экземпляр структуры или использовать new-инструкцию. Подходит только второй вариант, поскольку в этом случае вызывается конструктор по умолчанию (без аргументов), который инициализирует значениями по умолчанию поля структуры (в том числе и техническое поле, связанное с событием). При объявлении экземпляра структуры без new-инструкции этого не происходит.

    На следующем шаге мы рассмотрим структуры и интерфейсы.




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