• индексатор 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 EventReceiveri(ListWithChangedEvent list)
{
List = list;
// Присоединяет обработчик к событию.
OnConnect ();
}
//Обработчик события — выдает сообщение.
//Разрешает добавление элементов, меньших 10.
private void ListChanged(object sender,
ChangedEventArgs args)
{
Console.WriteLine("EventReceiveri: Сообщаю об изменениях: " + "Item ={0}", args.Item);
args.Permit = ((int)args.Item < 10);
}
public void OnConnect ()
{
//Присоединяет обработчик к событию
List.Changed += new ChangedEventHandler(ListChanged);
}
public void OffConnectO {
{
//Отсоединяет обработчик от события и удаляет список
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(listl);
Random rnd = new Random();
//Работа с объектами, приводящая к появлению событий
list.Add(rnd.Next(20)); list.Add(rnd.Next(2 0));
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 ();
}
В заключение взгляните на результаты работы этой процедуры.
Рис. 21.2. События в мире объектов
Две проблемы с обработчиками событий
Объекты, создающие события, ничего не знают об объектах, обрабатывающих эти события. Объекты, обрабатывающие события, ничего не знают друг о друге, независимо выполняя свою работу. В такой модели могут возникать определенные проблемы. Рассмотрим некоторые из них.
Игнорирование коллег
Задумывались ли вы, какую роль играет ключевое слово event, появляющееся при объявлении события? Событие, объявленное в классе, представляет экземпляр делегата. В предыдущей лекции, когда речь шла о делегатах, их экземпляры объявлялись без всяких дополнительных ключевых слов.
Слово "event" играет важную роль, позволяя решить проблему, названную нами "игнорированием коллег". В чем ее суть? В том, что некоторые из классов Receiver могут вести себя некорректно по отношению к своим коллегам, занимающимся обработкой того же события. При присоединении обработчика события в классе Receiver можно попытаться вместо присоединения обработчика выполнить операцию присваивания, игнорируя, тем самым, уже присоединенный список обработчиков. Взгляните еще раз на процедуру OnConnect класса Receiver2; там демонстрируется такая попытка в закомментированном операторе. Аналогично, в процедуре OffConnect вместо отсоединения (операции —) можно попытаться присвоить событию значение null, отсоединяя тем самым всех других обработчиков.