Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C# - лекции IntUit (Биллиг В.А.).pdf
Скачиваний:
140
Добавлен:
13.02.2015
Размер:
4.13 Mб
Скачать

вообще делегата не объявлять, а пользоваться стандартным делегатом с именем

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, если обработчик события дает добро на изменение элемента.

В модели, которую мы рассматриваем, предполагается, что обработчик события, получив уведомление об изменении элемента, анализирует ситуацию и может разрешить или не разрешить изменение, например, если значение элемента больше некоторого предельного значения.

Правильно ли, что обработчик события, а не сам класс, создающий событие, принимает решение о допуске изменения элемента списка? Все зависит от контекста. В прошлые времена молодые могли объявить о своей помолвке, но требовалось разрешение родителей на брак. Времена изменились - теперь на брак родительского благословения не требуется. Но в программистском мире ситуации, требующие внешнего разрешения, встречаются довольно часто.

Класс 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()

{

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]