Не обнаружил проекты, которые используются в примерах в лекции, также не увидел список задач. |
События
Классы receiver. Как обрабатываются события
Объекты класса Sender создают события и уведомляют о них объекты, возможно, разных классов, названных нами классами Receiver, или клиентами. Давайте разберемся, как должны быть устроены классы Receiver, чтобы вся эта схема заработала.
Понятно, что класс receiver должен:
- иметь обработчик события - процедуру, согласованную по сигнатуре с функциональным типом делегата, который задает событие;
- иметь ссылку на объект, создающий событие, чтобы получить доступ к этому событию - event-объекту;
- уметь присоединить обработчик события к event-объекту. Это можно реализовать по-разному, но технологично это делать непосредственно в конструкторе класса, так что когда создается объект, получающий сообщение, он изначально готов принимать и обрабатывать сообщения о событиях. Вот пример, демонстрирующий возможное решение проблем:
public class FireMen { private TownWithEvents MyNativeTown; public FireMen(TownWithEvents TWE) { this.MyNativeTown=TWE; MyNativeTown.FireEvent += new FireEventHandler(FireHandler); } private void FireHandler(object Sender, int time, int build) { Console.WriteLine("Fire at day {0}, in build {1}!", time, build); } public void GoOut() { MyNativeTown.FireEvent -= new FireEventHandler(FireHandler); } }//FireMan
В классе FireMen есть ссылка на объект класса TownWithEvents, создающий события. Сам объект передается в конструкторе класса. Здесь же происходит присоединение обработчика события к event-объекту. Обработчик события FireHandler выводит сообщение на консоль.
Классы с событиями, допустимые в каркасе .Net Framework
Если создавать повторно используемые компоненты с событиями, работающие не только в проекте C#, то необходимо удовлетворять некоторым ограничениям. Эти требования предъявляются к делегату; они носят, скорее, синтаксический характер, не ограничивая существа дела.
Перечислю эти ограничения:
- делегат, задающий тип события, должен иметь фиксированную сигнатуру из двух аргументов: delegate <Имя_делегата> (object sender, <Тип_аргументов> args);
- первый аргумент задает объект sender, создающий сообщение. Второй аргумент args задает остальные аргументы - входные и выходные, - передаваемые обработчику. Тип этого аргумента должен задаваться классом, производным от встроенного в .Net Framework класса EventArgs. Если обработчику никаких дополнительных аргументов не передается, то следует просто указать класс EventArgs, передавая null в качестве фактического аргумента при включении события;
- рекомендуемое имя делегата - составное, начинающееся именем события, после которого следует слово EventHandler, например, FireEventHandler. Если никаких дополнительных аргументов обработчику не передается, то тогда можно вообще делегат не объявлять, а пользоваться стандартным делегатом с именем EventHandler.
Пример "Списки с событиями"
В этом примере строится класс ListWithChangedEvent, являющийся потомком встроенного класса ArrayList, который позволяет работать со списками. В класс добавляется событие Changed, сигнализирующее обо всех изменениях элементов списка. Строятся два класса - Receiver1 и Receiver2, получающие сообщения. В примере рассматривается взаимодействие нескольких объектов: два объекта посылают сообщения, три - принимают.
Начнем с объявления делегата:
// Объявление делегата public delegate void ChangedEventHandler(object sender, ChangedEventArgs args);
Здесь объявлен делегат ChangedEventHandler, по всем правилам хорошего стиля - его имя и его форма соответствует всем требованиям. Второй аргумент, задающий аргументы события, принадлежит классу ChangedEventArgs, производному от встроенного класса EventArgs. Рассмотрим, как устроен этот производный класс:
public class ChangedEventArgs:EventArgs { private object item; private bool permit; public object Item { get {return(item);} set { item = value;} } public bool Permit { get {return(permit);} set { permit = value;} } }//class ChangedEventArgs
У класса два закрытых свойства, доступ к которым осуществляется через процедуры-свойства get и set. Конечно, можно было бы в данной ситуации сделать их просто public - общедоступными. Свойство Item задает входной аргумент события, передаваемый обработчику события. Булево свойство Permit задает выходной аргумент события, получающий в обработчике значение True, если обработчик события дает добро на изменение элемента.
В модели, которую мы рассматриваем, предполагается, что обработчик события, получив уведомление об изменении элемента, анализирует ситуацию и может разрешить или не разрешить изменение, например, если значение элемента больше некоторого предельного значения.
Правильно ли, что обработчик события, а не сам класс, создающий событие, принимает решение о допуске изменения элемента списка? Все зависит от контекста. В прошлые времена молодые могли объявить о своей помолвке, но требовалось разрешение родителей на брак. Времена изменились - теперь на брак родительского благословения не требуется. Но в программистском мире ситуации, требующие внешнего разрешения, встречаются довольно часто. |