КАТЕГОРИИ:
АстрономияБиологияГеографияДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРиторикаСоциологияСпортСтроительствоТехнологияФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника
|
Как реализуются событияНаучившись определять класс с членом-событием, пора поближе познакомиться с самим событием и узнать, как оно работает. В классе MailManager есть строчка кода, определяющая сам член-событие: public event EventHandler<NewMailEventArgs> NewMail; При компиляции этой строчки компилятор превращает ее в следующие три конструкции: // 1. ЗАКРЫТОЕ поле делегата, инициализированное null. private EventHandler<NewMailEventArgs> NewMail = null;
// 2. ОТКРЫТЫЙ метод add_Xxx (где Xxx - это имя события). // Позволяет объектам регистрироваться для получения уведомлений о событии. [MethodImpl(MethodImplOptions.Synchronized)] public void add_NewMail(EventHandler<NewMailEventArgs> value) { NewMail = (EventHandler<NewMailEventArgs>)Delegate.Combine(NewMail, value); }
// 3. ОТКРЫТЫЙ метод remove_Xxx (где Ххх - это имя события). // Позволяет объектам отменять регистрацию для получения уведомлений о событии [MethodImpl(MethodImplOptions.Synchronized)] public void remove_NewMail(EventHandler<NewMailEventArgs> value) { NewMail = (EventHandler<NewMailEventArgs>)Delegate.Remove(NewMail, value); }
Первая конструкция — просто поле соответствующего типа делегата. Оно содержит ссылку на заголовок списка делегатов, которые будут уведомляться при возникновении события. Поле инициализируется значением null, что означает, что нет получателей, ожидающих уведомления о событии. Когда метод регистрирует получателя уведомления, это поле ссылается на экземпляр делегата EventHandler<NewMailEventArgs>, который может в свою очередь ссылаться на дополнительные делегаты EventHandler<NewMailEventArgs>. Когда получатель регистрируется для получения уведомления о событии, он просто добавляет в список экземпляр типа делегата. Ясно, что отказ от регистрации означает удаление соответствующего делегата. Обратите внимание: в примере поле делегата, NewMail, всегда закрытое, несмотря на то, что исходная строка кода определяет событие как открытое. Причина в предотвращении доступа из кода, не относящегося к определяющему классу. Если бы поле было открытым, любой код мог бы изменить значение поля, в том числе удалить все делегаты, подписавшиеся на событие. Вторая создаваемая компилятором C# конструкция — метод, позволяющий другим объектам регистрироваться на получение уведомления о событии. Компилятор C# автоматически присваивает этой функции имя, добавляя приставку add_ к имени события (NewMail). Компилятор C# также автоматически генерирует код метода, который всегда вызывает статический метод Combine типа System.Delegate. Метод Combine добавляет в список делегатов новый экземпляр и возвращает новый заголовок списка, который снова сохраняется в поле. Третья и последняя создаваемая компилятором C# конструкция представляет собой метод, позволяющий объекту отказаться от подписки на событие. И этой функции компилятор С# присваивает имя автоматически, добавляя приставку remove_ к имени события (NewMail). Код метода всегда вызывает метод Remove типа System.Delegate. Последний метод удаляет делегат из списка и возвращает новый заголовок списка, который сохраняется в поле. Заметим также, что оба метода — add и remove — помечены атрибутом MethodImplAttribute (определенным в пространстве имен SystemRuntime.CompilerServices). Точнее, эти методы помечены как синхронизированные, поэтому они не нарушают безопасность потоков: множество объектов, следящих за событием, может регистрироваться или отменять свою регистрацию одновременно, не нарушая целостность связного списка. В этом примере методы add и remove являются открытыми, поскольку в соответствующей строке исходного кода событие изначально объявлено как открытое. Если бы оно было объявлено как закрытое, то add и remove, сгенерированные компилятором, тоже были бы объявлены как закрытые. Так что, когда в типе определяется событие, модификатор доступа события определяет, какой код способен регистрироваться и отменять регистрацию для уведомления о событии, но прямым доступом к полю делегата обладает только сам тип. Члены-события также могут объявляться статическими и виртуальными; в этом случае сгенерированные компилятором методы add и remove также будут статическими или виртуальными соответственно. Помимо генерации этих трех конструкций, компиляторы также генерируют запись с определением события и помещают ее в метаданные управляемого модуля. Эта запись содержит ряд флагов и базовый тип-делегат, а также ссылки на методы-аксессоры add и remove. Эта информация нужна просто для того, чтобы очертить связь между абстрактным понятием «событие» и его методами-аксессорами. Эти метаданные могут использовать компиляторы и другие инструменты, и, конечно же, эти сведения можно получить при помощи класса SystemReflec-tionEventlnfo. Однако сама CLR не использует эти метаданные и во время выполнения требует лишь методы-аксессоры.
|