Пользовательские события и методы доступа к событиям в компонентах среды выполнения Windows
Поддержка Среда выполнения Windows в .NET Framework упрощает объявление событий в компонентах Среда выполнения Windows, скрывая различия между шаблоном событий Среда выполнения Windows и шаблоном событий .NET Framework. Однако при объявлении пользовательских методов доступа к событиям в компоненте Среда выполнения Windows необходимо соблюдать шаблон Среда выполнения Windows.
При регистрации обработки события в Среда выполнения Windows метод доступа add возвращает токен. Для отмены регистрации этот токен передается в метод доступа remove. Это означает, что сигнатуры методов доступа add и remove событий Среда выполнения Windows отличаются от сигнатур методов доступа, к которым вы привыкли.
К счастью, компиляторы Visual Basic и C# упрощают процесс: при объявлении события с пользовательскими методами доступа в компоненте Среда выполнения Windows компиляторы автоматически используют шаблон Среда выполнения Windows. Например, если метод доступа add не возвращает токен, возникает ошибка компилятора. В .NET Framework имеется два типа для поддержки реализации:
Структура EventRegistrationToken представляет токен.
Класс EventRegistrationTokenTable<T> создает токены и поддерживает сопоставление между токенами и обработчиками событий. Аргумент универсального типа является типом аргумента события. Экземпляр этого класса создается для каждого события при первой регистрации обработчика этого события.
В следующем коде события NumberChanged показан основной шаблон событий Среда выполнения Windows. В этом примере конструктор объекта аргумента события, NumberChangedEventArgs, принимает один целочисленный параметр, который представляет измененное числовое значение.
Примечание
Такой же шаблон компиляторы используют для обычных событий, объявляемых в компоненте Среда выполнения Windows.
private EventRegistrationTokenTable<EventHandler<NumberChangedEventArgs>>
m_NumberChangedTokenTable = null;
public event EventHandler<NumberChangedEventArgs> NumberChanged
{
add
{
return EventRegistrationTokenTable<EventHandler<NumberChangedEventArgs>>
.GetOrCreateEventRegistrationTokenTable(ref m_NumberChangedTokenTable)
.AddEventHandler(value);
}
remove
{
EventRegistrationTokenTable<EventHandler<NumberChangedEventArgs>>
.GetOrCreateEventRegistrationTokenTable(ref m_NumberChangedTokenTable)
.RemoveEventHandler(value);
}
}
internal void OnNumberChanged(int newValue)
{
EventHandler<NumberChangedEventArgs> temp =
EventRegistrationTokenTable<EventHandler<NumberChangedEventArgs>>
.GetOrCreateEventRegistrationTokenTable(ref m_NumberChangedTokenTable)
.InvocationList;
if (temp != null)
{
temp(this, new NumberChangedEventArgs(newValue));
}
}
Private m_NumberChangedTokenTable As _
EventRegistrationTokenTable(Of EventHandler(Of NumberChangedEventArgs))
Public Custom Event NumberChanged As EventHandler(Of NumberChangedEventArgs)
AddHandler(ByVal handler As EventHandler(Of NumberChangedEventArgs))
Return EventRegistrationTokenTable(Of EventHandler(Of NumberChangedEventArgs)).
GetOrCreateEventRegistrationTokenTable(m_NumberChangedTokenTable).
AddEventHandler(handler)
End AddHandler
RemoveHandler(ByVal token As EventRegistrationToken)
EventRegistrationTokenTable(Of EventHandler(Of NumberChangedEventArgs)).
GetOrCreateEventRegistrationTokenTable(m_NumberChangedTokenTable).
RemoveEventHandler(token)
End RemoveHandler
RaiseEvent(ByVal sender As Class1, ByVal args As NumberChangedEventArgs)
Dim temp As EventHandler(Of NumberChangedEventArgs) = _
EventRegistrationTokenTable(Of EventHandler(Of NumberChangedEventArgs)).
GetOrCreateEventRegistrationTokenTable(m_NumberChangedTokenTable).
InvocationList
If temp IsNot Nothing Then
temp(sender, args)
End If
End RaiseEvent
End Event
Метод static (Shared в Visual Basic) GetOrCreateEventRegistrationTokenTable создает экземпляр объекта EventRegistrationTokenTable<T> события "ленивым" образом. Передайте в этот метод поле уровня класса, в котором будет храниться экземпляр таблицы токенов. Если поле оставлено пустым, метод создает таблицу, сохраняет ссылку ссылку на таблице в поле и возвращает ссылку на эту таблицу. Если поле уже содержит ссылку на таблицу токенов, то метод просто возвращает эту ссылку.
Важно!
Для обеспечения потокобезопасности поле, хранящее экземпляр объекта EventRegistrationTokenTable<T> события, должно быть полем уровня класса. Если поле является полем уровнем класса, метод GetOrCreateEventRegistrationTokenTable гарантирует, что при попытке создания таблицы токенов несколькими потоками, все потоки получат один и тот же экземпляр таблицы. Для заданного события все вызовы метода GetOrCreateEventRegistrationTokenTable должны использовать одно и то же поле уровня класса.
Вызов метода GetOrCreateEventRegistrationTokenTable в методе доступа remove и в методе RaiseEvent (метод OnRaiseEvent в C#) обеспечивает отсутствие исключений, если методы вызывают до добавления делегатов каких-либо обработчиков событий.
Другие члены класса EventRegistrationTokenTable<T>, используемые в шаблоне событий Среда выполнения Windows, включают следующее:
метод AddEventHandler создает токен для делегата обработчика событий, сохраняет делегат в таблице, добавляет его в список вызовов и возвращает токен;
перегрузка метода RemoveEventHandler(EventRegistrationToken) удаляет делегат из таблицы и из списка вызовов;
Примечание
Методы AddEventHandler и RemoveEventHandler(EventRegistrationToken) блокируют таблицу для обеспечения потокобезопасности.
свойство InvocationList возвращает делегат, который включает все обработчики событий, которые в настоящее время зарегистрированы для обработки события. Используйте этот делегат для вызова события или используйте методы класса Delegate, чтобы вызывать обработчики по-отдельности.
Примечание
Рекомендуется следовать шаблону, показанному в примере ранее в этой статье, и копировать делегат во временную переменную перед его вызовом. Это позволяет избежать состояния гонки, при котором один поток удаляет последний обработчик, превращая делегат в null непосредственно перед тем, как другой поток попытается вызвать его. Делегаты являются неизменяемыми, поэтому копия остается действительной.
Разместите свой код в методах доступа соответствующим образом. Если потокобезопасность важна, вы должны обеспечить собственную блокировку кода.
Пользователи C#: при написании пользовательских методов доступа к событиям в соответствии с шаблоном событий Среда выполнения Windows компилятор не поддерживает обычные синтаксические сокращения. При использовании в коде имени события компилятор возвращает ошибку.
Пользователи Visual Basic: в .NET Framework событие является просто многоадресным делегатом, который представляет все зарегистрированные обработчики событий. Создание события означает просто вызов делегата. Синтаксис Visual Basic обычно скрывает взаимодействия с делегатом, а компилятор копирует делегат перед его вызовом, как сказано в примечании о потокобезопасности. При создании пользовательского события в компоненте Среда выполнения Windows необходимо работать с делегатом напрямую. Это также означает, что можно, например, с помощью метода MulticastDelegate.GetInvocationList получать массив, содержащий отдельный делегат для каждого обработчика событий, если нужно вызывать обработчики отдельно.
См. также
Задачи
Ссылки
События (Руководство по программированию в C#)
Другие ресурсы
.NET Framework Support for Windows Store Apps and Windows Runtime