Здравствуйте! Записался на ваш курс, но не понимаю как произвести оплату. Надо ли писать заявление и, если да, то куда отправлять? как я получу диплом о профессиональной переподготовке? |
Классы с событиями
Класс sender
Рассмотрим, как устроен в нашем примере класс, создающий события. Начнем определение класса с задания его свойств и конструктора:
/// <summary> /// Класс, создающий событие. /// Потомок класса ArrayList. /// </summary> public class ListWithChangedEvent: ArrayList { string name; //имя объекта public event ChangedEventHandler Changed; //событие bool permit; //результат обработки события /// <summary> /// Конструктор /// </summary> /// <param name="name">имя объекта</param> public ListWithChangedEvent(string name) { this.name = name; } public string Name { get { return name; } }
Первое свойство задает имя объекта, чтобы обработчики могли узнать, кто послал сообщение. Второе свойство описывает событие Changed. Оно открыто, что позволяет присоединять к нему обработчиков событий. Третье свойство задает суммарный итог, сформированный на основании результатов работы всех обработчиков события.
Хороший стиль требует задания в классе процедуры On, включающей событие. Так и поступим:
/// <summary> /// Процедура On, включающая событие /// </summary> /// <param name="args">аргументы события</param> protected virtual void OnChanged(ChangedEventArgs args) { if (Changed != null) Changed(this, args); }
Процедура OnChanged соответствует ранее описанному образцу. Если список обработчиков не пуст, то зажигается событие - посылается сообщение всем обработчикам события. Синтаксически конструкция Changed(this, args) - это вызов списка методов, поскольку Changed - объект функционального типа, к которому прикреплен список последовательно работающих методов.
Если в списке аргументов args есть выходные аргументы, то для решения проблемы коллизии совместной работы обработчиков посылку сообщения обработчикам события следует устроить более сложным образом:
protected virtual void OnChanged(ChangedEventArgs args) { int countYes = 0, countNo = 0; if (Changed != null) { foreach (ChangedEventHandler del in Changed.GetInvocationList()) { del(this, args); if (args.Permit) countYes++; else countNo++; } permit = (countYes >= countNo); } }
Метод GetInvocationList позволяет получить список обработчиков события, а цикл foreach - вызывать один обработчик за другим. После того, как обработчик завершится, можно понять, каково значение сформированного выходного аргумента события. В данном примере окончательное решение принимается по большинству голосов. Счетчики countYes и countNo считают голоса "за" и "против". Представленное здесь решение демонстрирует корректный способ работы с событиями, когда обработчикам необходимо передавать входные и выходные аргументы.
Перейдем теперь к рассмотрению того, как в нашем классе возникают события. Наш класс, являясь наследником класса ArrayList, наследует все его методы. Переопределим методы, изменяющие элементы:
- метод Add, добавляющий новый элемент в конец списка;
- индексатор this, дающий доступ к элементу списка по индексу;
- метод Clear, производящий чистку списка.
// Переопределяемые методы, вызывающие событие Changed public override int Add(object value) { int index = -1; ChangedEventArgs evargs = new ChangedEventArgs(name, value); OnChanged(evargs); if (permit) index = base.Add(value); return index; }
Обратите внимание на схему включения события в процедуре Add. Вначале создается объект evargs - аргументы события, который передается методу OnChanged. Этот метод поочередно вызовет обработчики события и сформирует итоговый результат их работы. Анализ переменной permit позволяет установить, получено ли разрешение на изменение значения. При истинности значения этой переменной вызывается родительский метод Add, осуществляющий изменение значения. Аналогично устроены и другие методы, в которых возникает событие Changed.
public override void Clear() { ChangedEventArgs evargs = new ChangedEventArgs(name, 0); OnChanged(evargs); base.Clear(); } public override object this[int index] { set { ChangedEventArgs evargs = new ChangedEventArgs(name, value); OnChanged(evargs); if (permit) base[index] = value; } get {return(base[index]);} }
Это достаточно типичная схема организации класса с событиями.
Классы receiver
Построим два класса, объекты которых способны получать и обрабатывать событие Changed, возникающее у объектов класса ListWithChangedEvent. Вначале разберемся с устройством одного из этих классов, названного Receiver1. Вот его код:
class Receiver1 { private ListWithChangedEvent list; /// <summary> /// Конструктор /// Присоединяет обработчик к событию /// </summary> /// <param name="list">объект, посылающий сообщение</param> public Receiver1(ListWithChangedEvent list) { this.list = list; // Присоединяет обработчик к событию. list.Changed += new ChangedEventHandler(ListChanged); } public void OffConnect() { // Отсоединяет обработчик list.Changed -= new ChangedEventHandler(ListChanged); } }//class Receiver1
Класс Receiver1 является примером класса, чьи объекты настроены на прием сообщения от одного объекта класса ListWithChangedEvent. Такой объект передается конструктору класса, который и выполняет всю необходимую работу, присоединяя обработчик события к событию переданного конструктору объекта, так что вызов конструктора класса Receiver1 приводит к созданию объекта, способного принимать и обрабатывать сообщения от объекта, задающего список.
Давайте подробнее рассмотрим устройство обработчика события:
private void ListChanged(object sender, ChangedEventArgs args) { Console.WriteLine("Сообщение послал {0}, " + "элемент = {1}. " + " Сообщение получил: Receiver1 - ", args.Name, args.Item); if(args.Permit = (int)args.Item < 10) Console.WriteLine("Изменения разрешаю"); else Console.WriteLine("Изменения не разрешаю"); }
С входными аргументами все просто. Они используются для формирования сообщения и формирования принимаемого решения. Но стоит подробнее разобраться, как в данном конкретном случае формируется выходной аргумент. Свое решение "Разрешить или не разрешить изменение" обработчик принимает в зависимости от величины элемента ( item ). Свое решение обработчик события принимает независимо, без оглядки на методы, совместно с ним обрабатывающими то же событие.
Класс Receiver2 в отличие от класса Receiver1 позволяет слушать и обрабатывать сообщения нескольких объектов класса sender. С конструктора класса снимается задача связывания события с обработчиком события. Ему не нужно теперь передавать объект класса sender. У класса появляется специальный метод OnConnect, которому передается объект класса sender. Присоединение обработчика события к событию объекта sender выполняется при каждом вызове метода OnConnect.
События, приходящие от разных объектов, могут обрабатываться одним обработчиком класса receiver. Возможно существование в классе набора обработчиков событий, чтобы разные объекты могли иметь и разные методы, обрабатывающие приходящее сообщение о возникшем событии. Так строятся инерфейсные классы, где у каждой командной кнопки свой обработчик события. В нашем примере предусмотрен один обработчик для всех объектов одного класса.
class Receiver2 { void ListChanged(object sender, ChangedEventArgs args) { Console.WriteLine("Сообщение послал {0}, " + "элемент = {1}. " + " Сообщение получил: Receiver2 - ", args.Name, args.Item); if (args.Permit =(int)args.Item < 20) Console.WriteLine("Изменения разрешаю"); else Console.WriteLine("Изменения не разрешаю"); } public void OnConnect(ListWithChangedEvent list) { list.Changed += new ChangedEventHandler(ListChanged); //list.Changed = new ChangedEventHandler(ListChanged); } public void OffConnect(ListWithChangedEvent list) { list.Changed -= new ChangedEventHandler(ListChanged); //list.Changed = null; } }//class Receiver2
Классы созданы, теперь осталось создать объекты и заставить их взаимодействовать, чтобы одни создавали события, а другие их обрабатывали. Эту часть работы будет выполнять тестирующая процедура класса Testing:
public void TestChangeList() { // Создаются два объекта, вырабатывающие события ListWithChangedEvent list1 = new ListWithChangedEvent("list1"); ListWithChangedEvent list2 = new ListWithChangedEvent("list2"); // Создаются два объекта классов Receiver1 и Receiver2, //способные обрабатывать события класса ListWithChangedEvent Receiver1 receiver1 = new Receiver1(list1); Receiver2 receiver2 = new Receiver2(); receiver2.OnConnect(list1); receiver2.OnConnect(list2); // Работа с объектами, приводящая к появлению событий Random rnd = new Random(); list1.Add(rnd.Next(20)); list1.Add(rnd.Next(20)); list1.Add(33); list1[1] = 17; list2.Add(10); list2[0] = 25; list2.Clear(); //Отсоединение обработчика событий receiver1.OffConnect(); list1.Add(21); list1.Clear(); }
В тестирующей процедуре моделируется процесс работы с объектами, посылающими сообщения о событиях и принимающими эти сообщения. Два созданных объекта list1 и list2 посылают сообщения о событиях всякий раз, когда в список добавляется новый элемент или изменяется значение существующего элемента. Два созданных объекта receiver1 и receiver2 получают приходящие сообщения. Первый из них получает сообщения только от объекта list1, второй - от двух объектов list1 и list2. В некоторых ситуациях оба получателя сообщений "дают добро" на изменение элемента, в других ситуациях оба запрещают изменения.
В заключение взгляните на результаты работы этой процедуры.