
- •Лекция: Делегаты. Определение и обработка событий Функциональный тип в c#. Делегаты
- •Делегаты
- •Ковариантность и контравариантность
- •Классы с событиями
- •Класс sender. Как объявляются события?
- •Делегаты и события
- •Как зажигаются события
- •Классы receiver. Как обрабатываются события
- •Классы с событиями, допустимые в каркасе .Net Framework
- •Пример "Списки с событиями"
- •Класс sender
- •Классы receiver
- •Две проблемы с обработчиками событий
- •Игнорирование коллег
- •Переопределение значений аргументов события
Класс sender
Рассмотрим теперь, как устроен в нашем примере класс, создающий события. Начнем со свойств класса:
// Класс, создающий событие. Потомок класса ArrayList.
public class ListWithChangedEvent: ArrayList
{
//Свойства класса: событие и его аргументы
//Событие Changed, зажигаемое при всех изменениях
//элементов списка.
public event ChangedEventHandler Changed;
//Аргументы события
private ChangedEventArgs evargs = new ChangedEventArgs();
Первое свойство описывает событие Changed. Оно открыто, что позволяет присоединять к нему обработчиков событий. Второе закрытое свойство определяет аргументы события, передаваемые обработчикам.
Хороший стиль требует задания в классе процедуры On, включающей событие. Так и поступим:
//Методы класса: процедура On и переопределяемые методы.
//Процедура On, включающая событие
protected virtual void OnChanged(ChangedEventArgs args)
{
if (Changed != null)
Changed(this, args);
}
Процедура OnChanged полностью соответствует ранее описанному образцу, поэтому не требует дополнительных комментариев.
Наш класс, являясь наследником класса ArrayList, наследует все его методы. Переопределим методы, изменяющие элементы:
• метод Add, добавляющий новый элемент в конец списка;
• индексатор this, дающий доступ к элементу списка по индексу;
• метод Clear, производящий чистку списка.
//Переопределяемые методы, вызывающие событие Changed
//Добавление нового элемента
//при получении разрешения у обработчиков события
public override int Add(object value)
{
int i=0;
evargs.Item = value;
OnChanged(evargs);
if (evargs.Permit)
i = base.Add(value);
else
Console.WriteLine("Добавление элемента запрещено." +
"Значение = {0}", value);
return i;
}
public override void Clear()
{
evargs.Item=0;
OnChanged(evargs);
base.Clear();
}
public override object this[int index]
{
set
{
evargs.Item = value;
OnChanged(evargs);
if (evargs.Permit)
base[index] = value;
else
Console.WriteLine("Замена элемента запрещена." +
" Значение = {0}", value);
}
get{return(base[index]);}
}
Обратите внимание на схему включения события, например, в процедуре Add. Вначале задаются входные аргументы, в данном случаеItem. Затем вызывается процедура включенияOnChanged. При зажигании выполнение процедурыAdd прерывается. Запускаются обработчики, присоединенные к событию. ПроцедураAdd продолжит работу только после окончания их работы. Анализ выходной переменнойPermit позволяет установить, получено ли разрешение на изменение значения; при истинности значения этой переменной вызывается родительский методAdd, осуществляющий изменение значения. Это достаточно типичная схема работы с событиями.
Классы receiver
Мы построим два класса, объекты которых способны получать и обрабатывать событие Changed. Получать они будут одно и то же сообщение, а обрабатывать его будут по-разному. В нашей модельной задаче различие обработчиков сведется к выдаче разных сообщений. Поэтому достаточно разобраться с устройством одного класса, названногоEventReceiver1. Вот его код:
class EventReceiver1
{
private ListWithChangedEvent List;
public EventReceiver1(ListWithChangedEvent list)
{
List = list;
// Присоединяет обработчик к событию.
OnConnect();
}
//Обработчик события - выдает сообщение.
//Разрешает добавление элементов, меньших 10.
private void ListChanged(object sender,
ChangedEventArgs args)
{
Console.WriteLine("EventReceiver1: Сообщаю об
изменениях:" + "Item ={0}", args.Item);
args.Permit = ((int)args.Item < 10);
}
public void OnConnect()
{
//Присоединяет обработчик к событию
List.Changed += new ChangedEventHandler(ListChanged);
}
public void OffConnect()
{
//Отсоединяет обработчик от события и удаляет список
List.Changed -= new ChangedEventHandler(ListChanged);
List = null;
}
}//class EventReceiver1
Краткие комментарии.
• Среди закрытых свойств класса есть ссылка List на объект, создающий события.
• Конструктору класса передается фактический объект, который и будет присоединен к List. В конструкторе же происходит присоединение обработчика события к событию. Для этого, как положено, используется созданный в классе метод OnConnect.
• Класс содержит метод OffConnect, позволяющий при необходимости отключить обработчик от события.
• Обработчик события, анализируя переданный ему входной аргумент события Item, разрешает или не разрешает изменение элемента, формируя значение выходного аргумента Permit. Параллельно обработчик выводит на консоль сообщение о своей работе.
Класс Receiver2 устроен аналогично. Приведу его текст уже без всяких комментариев:
class Receiver2
{
private ListWithChangedEvent List;
public Receiver2(ListWithChangedEvent list)
{
List = list;
// Присоединяет обработчик к событию.
OnConnect();
}
// Обработчик события - выдает сообщение.
//Разрешает добавление элементов, меньших 20.
private void ListChanged(object sender,
ChangedEventArgs args)
{
Console.WriteLine("Receiver2: Сообщаю об изменениях:"
+ " Объект класса {0} : " + "Item ={1}",
sender.GetType(), args.Item);
args.Permit = ((int)args.Item < 20);
}
public void OnConnect()
{
//Присоединяет обработчик к событию
List.Changed += new ChangedEventHandler(ListChanged);
//Заметьте, допустимо только присоединение (+=),
//но не замена (=)
//List.Changed = new ChangedEventHandler(ListChanged);
}
public void OffConnect()
{
//Отсоединяет обработчик от события и удаляет список
List.Changed -= new ChangedEventHandler(ListChanged);
List = null;
}
}//class Receiver2
Классы созданы, теперь осталось создать объекты и заставить их взаимодействовать, чтобы одни создавали события, а другие их обрабатывали. Эту часть работы будет выполнять тестирующая процедура класса Testing:
public void TestChangeList()
{
//Создаются два объекта, вырабатывающие события
ListWithChangedEvent list = new ListWithChangedEvent();
ListWithChangedEvent list1 = new ListWithChangedEvent();
//Создаются три объекта двух классов EventReceiver1 и
//Receiver2, способные обрабатывать события класса
//ListWithChangedEvent
EventReceiver1 Receiver1 = new EventReceiver1(list);
Receiver2 Receiver21 = new Receiver2 (list);
Receiver2 Receiver22 = new Receiver2(list1);
Random rnd = new Random();
//Работа с объектами, приводящая к появлению событий
list.Add(rnd.Next(20)); list.Add(rnd.Next(20));
list[1] =17;
int val = (int)list[0] + (int)list[1];list.Add(val);
list.Clear();
list1.Add(10); list1[0] = 25; list1.Clear();
//Отсоединение обработчика событий
Receiver1.OffConnect();
list.Add(21); list.Clear();
}
В заключение взгляните на результаты работы этой процедуры.