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

22. События

Событийно-ориентированное программирование(СОП) – парадигма программирования, в которой выполнение программы определяетсясобытиями– действиями пользователя (клавиатура, мышь), сообщениями других программ и потоков, событиями операционной системы. СОП, как правило, применяется при построении пользовательских интерфейсов и при программировании игр, в которых осуществляется управление множеством объектов.

Работу с событиями можно условно разделить на три этапа:

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

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

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

В языке C# событие можно объявить в пределах класса или структуры. Для этого используется ключевое словоeventи делегат, описывающий метод обработки события. Обычно этот делегат имеет тип возвращаемого значенияvoid1.

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

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

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

{

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

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

};

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

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

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

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

publicclassValueEventArgs:EventArgs

{

publicintNewValue {get;privateset; }

publicValueEventArgs(intnewValue)

{

NewValue = newValue;

}

}

publicclassExampleClass

{

privateint_field;

publicintField

{

get{return_field; }

set

{

_field = value;

if (value< 0)

{

OnNegativeValueSet(newValueEventArgs(value));

}

}

}

protectedvirtualvoidOnNegativeValueSet(ValueEventArgse)

{

EventHandler<ValueEventArgs> local = NegativeValueSet;

if(local !=null)

{

local(this, e);

}

}

publiceventEventHandler<ValueEventArgs> 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);

}