Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CSharp Language Specification.doc
Скачиваний:
13
Добавлен:
26.09.2019
Размер:
4.75 Mб
Скачать

10.8.1События, подобные полям

В тексте программы в классе или структуре, содержащих объявление события, некоторые события можно использовать как поля. Для такого использования событие не должно быть abstract или extern и не должно явно включать объявления_методов_доступа_к_событиям. Такое событие можно использовать в любом контексте, где разрешено использование поля. Поле содержит делегат (§15), ссылающийся на список обработчиков событий, добавленных к событию. Если обработчики событий не добавлены, поле содержит null.

В примере

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

public class Button: Control { public event EventHandler Click;

protected void OnClick(EventArgs e) { if (Click != null) Click(this, e); }

public void Reset() { Click = null; } }

Событие Click используется как поле внутри класса Button. Как показано в примере, это поле можно проверять, изменять и использовать в выражениях вызова делегата. Метод OnClick в классе Button "вызывает" событие Click. Понятие вызова события совершенно эквивалентно вызову делегата, представленного событием. Поэтому не существует специальных языковых конструкций для вызова событий. Обратите внимание, что вызову делегата предшествует проверка, что делегат не равен null.

Вне объявления класса Button член Click может использоваться только с левой стороны операторов += и –=, как в следующем примере.

b.Click += new EventHandler(…);

В этом примере делегат добавляется к списку вызовов события Click, а в примере

b.Click –= new EventHandler(…);

делегат удаляется из списка вызовов события Click.

При компиляции события, подобного полю, компилятор автоматически создает хранилище для хранения делегата и создает методы доступа для события, добавляющие или удаляющие обработчики событий поля делегата. Чтобы быть потокобезопасными, операции добавления и удаления выполняются при блокировке (§8.12) содержащего объекта для события экземпляра или объекта типа (§7.6.10.6) для статического события.

Так, объявление события экземпляра вида:

class X { public event D Ev; }

можно скомпилировать в нечто, эквивалентное:

class X { private D __Ev; // field to hold the delegate

public event D Ev { add { lock(this) { __Ev = __Ev + value; } }

remove { lock(this) { __Ev = __Ev - value; } } } }

Внутри класса X ссылки на Ev компилируются в ссылки на скрытое поле __Ev. Имя "__Ev" произвольное; скрытое поле могло бы иметь любое имя или вообще не иметь никакого имени.

Аналогично объявление статического события вида:

class X { public static event D Ev; }

можно откомпилировать в нечто, эквивалентное:

class X { private static D __Ev; // field to hold the delegate

public static event D Ev { add { lock(typeof(X)) { __Ev = __Ev + value; } }

remove { lock(typeof(X)) { __Ev = __Ev - value; } } } }

10.8.2Методы доступа к событиям

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

Объявления_методов_доступа_к_событиям события указывают исполняемые операторы, связанные с добавлением и удалением обработчиков событий.

Объявления методов доступа состоят из объявления_метода_доступа_add и объявления_метода_доступа_remove. Каждое объявление метода доступа состоит из лексемы add или remove, за которой следует блок. В блоке, связанном с объявлением_метода_доступа_add, указываются операторы, выполняемые при добавлении обработчика событий, а в блоке, связанном с объявлением_метода_доступа_remove, указываются операторы, выполняемые при удалении обработчика событий.

Каждое объявление_метода_доступа_add и объявление_метода_доступа_remove соответствует методу с одним параметром значения типа события и типом возвращаемого значения void. Неявный параметр метода доступа к событию называется value. Когда событие используется в назначении события, используется соответствующий метод доступа к событию. В частности, если оператором присваивания является +=, используется метод доступа add, а если оператор присваивания -=, используется метод доступа remove. В любом случае правый операнд оператора присваивания используется в качестве аргумента метода доступа к событию. Блок объявления_метода_доступа_add или объявления_метода_доступа_remove должен соответствовать правилам для методов void, описанным в §10.6.10. В частности, операторам return в таком блоке запрещено указывать выражение.

Поскольку в методе доступа к событию неявно имеется параметр с именем value, объявление в методе доступа к событию локальной переменной или константы с таким именем является ошибкой времени компиляции.

В примере

class Control: Component { // Unique keys for events static readonly object mouseDownEventKey = new object(); static readonly object mouseUpEventKey = new object();

// Return event handler associated with key protected Delegate GetEventHandler(object key) {...}

// Add event handler associated with key protected void AddEventHandler(object key, Delegate handler) {...}

// Remove event handler associated with key protected void RemoveEventHandler(object key, Delegate handler) {...}

// MouseDown event public event MouseEventHandler MouseDown { add { AddEventHandler(mouseDownEventKey, value); } remove { RemoveEventHandler(mouseDownEventKey, value); } }

// MouseUp event public event MouseEventHandler MouseUp { add { AddEventHandler(mouseUpEventKey, value); } remove { RemoveEventHandler(mouseUpEventKey, value); } }

// Invoke the MouseUp event protected void OnMouseUp(MouseEventArgs args) { MouseEventHandler handler; handler = (MouseEventHandler)GetEventHandler(mouseUpEventKey); if (handler != null) handler(this, args); } }

класс Control реализует механизм внутреннего хранилища для событий. Метод AddEventHandler связывает значение делегата с ключом, метод GetEventHandler возвращает делегат, в данный момент связанный с ключом, а метод RemoveEventHandler удаляет делегат в качестве обработчика событий для указанного события. Предположительно, лежащий в основе механизм хранилища разработан так, что отсутствуют затраты на связывание значения делегата null с ключом, и таким образом необрабатываемые события не расходуют емкость хранилища.

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