|
Здравствуйте! Записался на ваш курс, но не понимаю как произвести оплату. Надо ли писать заявление и, если да, то куда отправлять? как я получу диплом о профессиональной переподготовке? |
Классы с событиями
Класс 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. В некоторых ситуациях оба получателя сообщений "дают добро" на изменение элемента, в других ситуациях оба запрещают изменения.
В заключение взгляните на результаты работы этой процедуры.
