Студопедия

КАТЕГОРИИ:

АстрономияБиологияГеографияДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРиторикаСоциологияСпортСтроительствоТехнологияФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника


Явное управление регистрацией событий




Порой методы add и remove, сгенерированные компилятором, далеки от идеала. Ранее я уже говорил о всех проблемах, связанных с безопасностью потоков, существующих в компиляторе C# компании Microsoft. Вообще-то компилятор C# компании Microsoft никогда не обеспечивает максимальную безопасность, если речь идет о программировании безопасного и надежного кода. Чтобы создавать исключительно надежные компоненты, рекомендую всегда использовать прием, описанный в этом разделе — он позволит решить все проблемы безопасности потоков. Впрочем, его можно использовать и для решения других задач. В частности, очень распространенная причина, побуждающая программистов к самостоятельной реализации методов add и remove, — определение в типе множества событий, когда одновременно требуется эффективнее использовать память. В следующем разделе мы разберем такой сценарий подробнее.

К счастью, компилятор C#, как и многие другие, позволяет разработчикам явно реализовывать методы-аксессоры add и remove. Вот как можно модифицировать объект MailManager для обеспечения безопасной для потоков регистрации и отмены регистрации на уведомление о событии:

 

internal class MailManager

{

 

// Создаем закрытое зкземплярное поле

// для блокировки синхронизации потоков.

private readonly Object m_eventLock = new Object();

// Создаем закрытое поле, ссылающееся на заголовок списка делегатов,

private EventHandler<NewMailEventArgs> m_NewMail;

// Создаем в классе член-событие.

public event EventHandler<NewMailEventArgs> NewMail

{

// Явно реализуем метод add.

add

{

// Берем закрытую блокировку и добавляем обработчик

// (передаваемый по значению) в список делегатов,

lock (m_eventLock)

{

m_NewMail += value;

}

}

// Явно реализуем метод remove,

remove

{

// Берем закрытую блокировку и удаляем обработчик

// (передаваемый по значению) из списка делегатов,

lock (m_eventLock)

{

m_NewMail -= value;

}

}

}

// Определяем метод, отвечающий за инициирование события

// и информирование об этом зарегистрированных объектов.

// Если класс изолирован, определяем метод как закрытый и невиртуальный.

protected virtual void OnNewMail(NewMailEventArgs e)

{

// Сохраняем поле делегата во временном поле для обеспечения безопасности потоков.

EventHandler<NewMailEventArgs> temp = m_NewMail;

// Если есть зарегистрировавшиеся объекты,

// уведомляем их.

if (temp != null)

temp(this, е);

}

// Создаем метод, преобразующий входную информацию

// в требуемое событие.

public void SimulateNewMail(String from, String to, String subject)

{

// Создаем объект, хранящий информацию, которую нужно передать

// объектам, получающим уведомление о событии.

NewMailEventArgs е = new NewMailEventArgs(from, to, subject);

// Вызываем виртуальный метод, уведомляющий наш объект о возникновении события.

// Если нет типа, переопределяющего этот метод,

// наш объект уведомит все объекты, подписавшиеся

// на уведомление о событии.

OnNewMail(e);

}

}

 

В новой версии объекта MailManager закрытое поле m_NewMail, ссылающееся на список делегатов, должно определяться явно. В оригинальном синтаксисе события компилятор C# автоматически определял закрытое поле. В новом синтаксисе, в котором разработчик явно предоставляет реализацию методов-аксессоров add и remove, поля также должны объявляться явно.

Это поле просто представляет собой ссылку на делегат EventHandler<NewMailEventArgs>. Нет ничего, что превращает это поле в событие. В новом расширенном синтаксисе после ключевого слова event следует то, что собственно определяет событие в типе. В блоках add и remove содержится реализация методов-аксессоров. Заметьте: каждый метод принимает скрытый параметр по имени value типа EventHandler<NewMailEventArgs>. Код внутри методов выполняет все операции по добавлению и удаление делегатов из списка. В отличие от свойств, у которых есть оба или хотя бы один метод-аксессор get или set, у событий всегда должны быть оба метода-аксессора add и remove.

Явная реализация методов-аксессоров, показанная в предыдущем примере, работает как методы-аксессоры, сгенерированные компилятором C#, если не считать отсутствия атрибута [MethodImpl(MethodImplOptionsSynchronized)], а вместо этого используется оператор lock языка С# со ссылкой на закрытый определенный объект meventLock типа Object. Именно так я решаю проблему небезопасности потоков, о которой говорил в предыдущем разделе. Поскольку поле m_eventLock объявляется как закрытое, никакой код, за исключением кода класса MailManager, не в состоянии получить доступ к нему; за счет этого повышается надежность класса MailManager.

Событие можно объявлять как статический член, отчего методы-аксессоры также становятся статическими. Естественно, что закрытое поле mJSIewMail также станет статическим. В таком случае для обеспечения безопасности потоков поле m_eventLock также нужно объявить статическим. Это применимо как к ссылочным, так и значимым типам, в которых требуется предоставить статическое событие безопасно для потоков. К сожалению, как говорилось в предыдущем разделе, не существует хорошего способа сделать события экземпляров значимого типа безопасными с точки зрения потоков из-за того, что нет хорошего способа инициализировать экземплярное поле в значимом типе. За объяснением причин отсылаю вас к главе 5.

Код, регистрирующий и отменяющий регистрацию события, не различает методы add и remove, созданные автоматически компилятором или явно реализованные разработчиком. На самом деле это не исключает возможности использования в исходном тексте операторов += и -=, при этом компилятор будет знать, что следует сгенерировать вызовы явно определенных методов.

И последнее замечание относительно метода OnNewMail. Семантически этот метод идентичен предыдущей версии. Единственная разница в том, что имя события (NewMail) заменено на имя поля делегата, m_NewMail.

 


Поделиться:

Дата добавления: 2015-04-21; просмотров: 55; Мы поможем в написании вашей работы!; Нарушение авторских прав





lektsii.com - Лекции.Ком - 2014-2024 год. (0.006 сек.) Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав
Главная страница Случайная страница Контакты