Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Язык C# и основы платформы .NET.docx
Скачиваний:
57
Добавлен:
11.05.2015
Размер:
203.66 Кб
Скачать

22. События

События– способ описания связи одного объекта с другими по действиям. Работу с событиями можно условно разделить на три этапа:

объявлениесобытия (publishing);

регистрацияполучателя события (subscribing);

генерациясобытия (raising).

Событие можно объявить в пределах класса, структуры или интерфейса. Базовый синтаксис объявления события следующий:

модификаторы event тип-делегата имя-события;

Ключевое слово eventуказывает на объявление события. При объявлении события требуется указать делегат, описывающий метод обработки события. Обычно этот делегат имеет тип возвращаемого значенияvoid.

Фактически, события являются полями типа делегатов. При объявлении события компилятор добавляет в класс или структуру private-поле с именемимя-событияи типомтип-делегата. Кроме этого, для обслуживания события компилятор создаёт два методаadd_Name()иremove_Name(), гдеName– имя события. Эти методы содержат код, добавляющий и удаляющий обработчик события в цепочку группового делегата, связанного с событием.

Если программиста по каким-либо причинам не устраивает автоматическая генерация методов add_Name()иremove_Name(), он может описать собственную реализацию данных методов. Для этого при объявлении события указывается блок, содержащий секцииaddиremove:

модификаторы event тип-делегата имя-события

{

add { операторы }

remove { операторы }

};

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

Для генерации события в требуемом месте кода помещается вызов в формате имя-события(фактические-аргументы). Предварительно обычно проверяют, назначен ли обработчик события. Генерация события может происходить только в том же классе, в котором событие объявлено1.

Приведём пример класса, содержащего объявление и генерацию события. Данный класс будет включать метод с целым параметром, устанавливающий значение поля класса. Если значение параметра отрицательно, генерируется событие, определённое в классе:

public delegate void Handler(int val);

publicclassExampleClass

{

privateint_field;

publicintField

{

get

{

return_field;

}

set

{

_field = value;

if(value< 0)

{

// проверка нужна, чтобы предотвратить генерацию

// исключительной ситуации, если нет обработчика

if(NegativeValueSet !=null)

{

NegativeValueSet(value);

}

}

}

}

publiceventHandlerNegativeValueSet;

}

Рассмотрим этап регистрации получателя события. Чтобы отреагировать на событие, его надо ассоциировать собработчиком события. Обработчиком может быть метод, приводимый к типу события (делегату). В качестве обработчика может выступать анонимный метод или лямбда-оператор. Назначение и удаление обработчиков события выполняется при помощи операторов+=и-=.

Используем класс ExampleClassи продемонстрируем назначение и удаление обработчиков событий:

publicclassMainClass

{

publicstaticvoidReaction(inti)

{

Console.WriteLine("Negative value = {0}", i);

}

publicstaticvoidMain()

{

varc =newExampleClass();

c.Field = -10; // нет обработчиков, нет реакции на событие

// назначаем обработчик

c.NegativeValueSet += Reaction;

c.Field = -20; // вывод: "Negative value = -20"

// назначаем ещё один обработчик в виде лямбда-выражения

c.NegativeValueSet += i => Console.WriteLine(i);

c.Field = -30; // вывод: "Negative value = -30" и "-30"

// удаляем первый обработчик

c.NegativeValueSet -= Reaction;

}

}

Платформа .NET предлагает средства стандартизации работы с событиями. В частности, для типов событий зарезервированы следующие делегаты:

public delegate void EventHandler(object sender, EventArgs e);

publicdelegatevoidEventHandler<T>(objectsender, T e)

where T : EventArgs;

Как видим, данные делегаты предполагают, что первым параметром будет выступать объект, в котором событие было сгенерировано. Второй параметр используется для передачи информации события. Это либо класс EventArgs, либо наследник этого класса с необходимыми полями.

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

Внесём изменения в код класса ExampleClass, чтобы работа с событиями соответствовала стандартам:

publicclassMyEventArgs:EventArgs

{

publicintNewValue {get;privateset; }

publicMyEventArgs(intnewValue)

{

NewValue = newValue;

}

}

publicclassExampleClass

{

privateint_field;

publicintField

{

get{return_field; }

set

{

_field = value;

if (value< 0)

{

OnNegativeValueSet(newMyEventArgs(value));

}

}

}

protectedvirtualvoidOnNegativeValueSet(MyEventArgse)

{

EventHandler<MyEventArgs> local = NegativeValueSet;

if(local !=null)

{

local(this, e);

}

}

publiceventEventHandler<MyEventArgs> NegativeValueSet;

}

Создадим несколько полезных классов для упрощения работы с событиями. Очень часто для передачи информации события достаточно класса с единственным свойством. В этом случае можно использовать универсальный класс EventArgs<T>.

public class EventArgs<T> : EventArgs

{

publicT EventInfo {get;privateset; }

publicEventArgs(T eventInfo)

{

EventInfo = eventInfo;

}

}

Вот пример использования EventArgs<T>при описании события:

publiceventEventHandler<EventArgs<int>> NegativeValueSet;

Поместим в класс EventHelperдва метода расширения, упрощающих генерацию событий:

publicstaticclassEventHelper

{

publicstaticvoidRaise<T>(

thisEventHandler<EventArgs<T>> handler,

EventArgs<T> args,objectsender =null)

{

varlocal = handler;

if(local !=null)

{

local(sender, args);

}

}

publicstaticvoidRaise(thisEventHandlerhandler,

EventArgsargs,objectsender =null)

{

varlocal = handler;

if(local !=null)

{

local(sender, args);

}

}

}

Вот пример использования метода расширения из класса EventHelper:

protectedvirtualvoidOnNegativeValueSet(EventArgs<int> e)

{

NegativeValueSet.Raise(e, this);

}