Eventos personalizados e acessadores de evento nos componentes do Tempo de Execução do Windows

O suporte do .NET Framework para o Tempo de Execução do Windows facilita declarar eventos em componentes do Tempo de Execução do Windows ocultando as diferenças entre o padrão de eventos do Tempo de Execução do Windows e o padrão de eventos do .NET Framework. No entanto, quando você declara acessadores de evento personalizados em um componente do Tempo de Execução do Windows, deve seguir o padrão do Tempo de Execução do Windows.

Quando você se registra para manipular um evento no Tempo de Execução do Windows, o acessador add retorna um token. Ao cancelar o registro, você transmite esse token para o acessador remove. Isso significa que os acessadores add e remove para eventos do Tempo de Execução do Windows têm assinaturas diferentes dos acessadores que você conhece.

Felizmente, os compiladores do Visual Basic e C# simplificam o processo: quando você declara um evento com acessadores personalizados em um componente do Tempo de Execução do Windows, os compiladores usam automaticamente o padrão do Tempo de Execução do Windows. Por exemplo, você obterá um erro de compilador se o seu acessador add não retornar um token. O .NET Framework fornece dois tipos para oferecer suporte à implementação:

  • A estrutura EventRegistrationToken representa o token.

  • A classe EventRegistrationTokenTable<T> cria tokens e mantém um mapeamento entre tokens e manipuladores de eventos. O argumento de tipo genérico é o tipo de argumento de evento. Você cria uma instância dessa classe para cada evento na primeira vez que um manipulador de eventos é registrado para o evento.

O seguinte código para o evento NumberChanged mostra o padrão básico para eventos do Tempo de Execução do Windows. Nesse exemplo, o construtor para o objeto de argumento do evento, NumberChangedEventArgs, aceita um único parâmetro inteiro que representa o valor numérico alterado.

Dica

Esse é o mesmo padrão que os compiladores usam para os eventos comuns que você declara em um componente do Tempo de Execução do 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

O método GetOrCreateEventRegistrationTokenTable static (Shared no Visual Basic) cria a instância do evento do objeto EventRegistrationTokenTable<T> lentamente. Transmita o campo no nível de classe que conterá a instância de tabela de token para este método. Se o campo estiver vazio, o método criará a tabela, armazenará uma referência à tabela no campo e retornará uma referência para a tabela. Se o campo já contiver uma referência de tabela de token, o método retornará apenas essa referência.

Importante

Para garantir segurança de segmentos, o campo que contém a instância do evento EventRegistrationTokenTable<T> deve ser um campo no nível de classe. Se for um campo no nível de classe, o método GetOrCreateEventRegistrationTokenTable garantirá que quando vários segmentos tentarem criar a tabela de token, todos os segmentos obtenham a mesma instância da tabela. Para um determinado evento, todas as chamadas para o método GetOrCreateEventRegistrationTokenTable devem usar o mesmo campo no nível de classe.

Chamar o método GetOrCreateEventRegistrationTokenTable no acessador remove e o método RaiseEvent (o método OnRaiseEvent em C#) garante que nenhuma exceção ocorra se esses métodos forem chamados antes de todos os representantes do manipulador de eventos serem adicionados.

Os outros membros da classe EventRegistrationTokenTable<T> que são usados no padrão de eventos do Tempo de Execução do Windows incluem o seguinte:

  • O método AddEventHandler gera um token para o representante do manipulador de eventos, armazena o representante na tabela, adiciona-o à lista de invocação e retorna o token.

  • A sobrecarga do método RemoveEventHandler(EventRegistrationToken) remove o representante da tabela e da lista de invocação.

    Dica

    Os métodos AddEventHandler e RemoveEventHandler(EventRegistrationToken) bloqueiam a tabela para ajudar a garantir a segurança dos segmentos.

  • A propriedade InvocationList retorna um representante que inclui todos os manipuladores de eventos registrados no momento para manipular o evento. Use esse representante para gerar o evento, ou use os métodos da classe Delegate para invocar manipuladores individualmente.

    Dica

    Recomendamos que você siga o padrão mostrado no exemplo fornecido anteriormente neste artigo e copie o representante em uma variável temporária antes de chamá-lo. Isso evita uma condição de corrida em que o segmento remove o último manipulador, reduzindo o representante a null imediatamente antes que outro segmento tentar chamar o representante. Representantes são imutáveis, então, a cópia ainda é válida.

Coloque seu próprio código nos acessadores conforme apropriado. Se a segurança dos segmentos for um problema, você deve fornecer seu próprio bloqueio para seu código.

Usuários da linguagem C#: ao gravar acessadores de evento personalizados no padrão de eventos do Tempo de Execução do Windows, o compilador não fornece os atalhos sintáticos usuais. Ele gerará erros se você usar o nome do evento em seu código.

Usuários do Visual Basic: no .NET Framework, um evento é apenas um representante multicast que representa todos os manipuladores de eventos registrados. Disparar o evento significa apenas chamar o representante. Em geral, a sintaxe do Visual Basic oculta as interações com o representante, e o compilador copia o representante antes de chamá-lo, conforme descrito na nota sobre segurança de segmentos. Quando você cria um evento personalizado em um componente do Tempo de Execução do Windows, é necessário lidar com o representante diretamente. Isso também significa que você pode, por exemplo, usar o método MulticastDelegate.GetInvocationList para obter uma matriz que contém um representante separado para cada manipulador de eventos, se você desejar chamar manipuladores separadamente.

Consulte também

Tarefas

Como: implementar acessadores de evento personalizada (guia de programação do C#)

Referência

Eventos (guia de programação do C#)

Outros recursos

Eventos (Visual Basic)

Suporte do .NET Framework para aplicativos da Windows Store e Tempo de Execução do Windows