Pergunta

Estou procurando algumas dicas sobre a implementação de eventos personalizados no VB.NET (Visual Studio 2008, .NET 3.5).

Eu sei que os eventos "regulares" (não personalizados) são realmente delegados, então eu estava pensando em usar os delegados ao implementar um evento personalizado. Por outro lado, Andrew Troelsen's "Pro VB 2008 e a plataforma .NET 3.5" O livro usa tipos de coleção em todos os seus exemplos de eventos personalizados e na Microsoft's Códigos de amostra Combine essa linha de pensamento.

Então, minha pergunta é: que considerações devo ter ao escolher um design em detrimento do outro? Quais são os prós e os contras para cada design? Qual destes se assemelha à implementação interna de eventos "regulares"?

Abaixo está um código de amostra que demonstra os dois projetos.

Public Class SomeClass
    Private _SomeEventListeners As EventHandler
    Public Custom Event SomeEvent As EventHandler
        AddHandler(ByVal value As EventHandler)
            _SomeEventListeners = [Delegate].Combine(_SomeEventListeners, value)
        End AddHandler

        RemoveHandler(ByVal value As EventHandler)
            _SomeEventListeners = [Delegate].Remove(_SomeEventListeners, value)
        End RemoveHandler

        RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
            _SomeEventListeners.Invoke(sender, e)
        End RaiseEvent
    End Event

    Private _OtherEventListeners As New List(Of EventHandler)
    Public Custom Event OtherEvent As EventHandler
        AddHandler(ByVal value As EventHandler)
            _OtherEventListeners.Add(value)
        End AddHandler

        RemoveHandler(ByVal value As EventHandler)
            _OtherEventListeners.Remove(value)
        End RemoveHandler

        RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
            For Each handler In _OtherEventListeners
                handler(sender, e)
            Next
        End RaiseEvent
    End Event
End Class
Foi útil?

Solução

Eu diria que use o delegado simples; Simplesmente - é (internamente) uma lista, então você está duplicando o esforço, envolvendo -o List<T>. Você também tem a sobrecarga de objetos e matrizes de lista extra, que podem ser nulos, para que mais impacto na coleta de lixo etc.

Além disso, se você está fazendo grande quantidade disso, considere EventHandlerList, que existe para fornecer acesso eficiente a escasso Eventos - ou seja, onde você deseja expor muitos eventos, mas muitos deles podem não ser atribuídos.

O primeiro exemplo está muito mais próximo dos eventos "padrão" (embora você queira assistir a manipuladores não atribuídos / nulos ao ligar Invoke, como pode ser nulo). Além disso, observe que alguns idiomas (sinceramente não sei o que o VB faz aqui) aplica a sincronização aos eventos, mas na realidade muito poucos eventos verdade Precisa ser seguro para fios, para que isso possa ser um exagero.

editar também ocorre que há um funcional diferença entre eles:

  • A abordagem delegada trata diferentes instâncias com o mesmo método / instância de destino que igual (não acho List<T> vai)
  • Delegados duplicados: o delegado remove o último primeiro; A lista remove mais cedo
  • Compostos: adicione a, adicione b e remova (a+b) - Com um delegado, isso deve produzir nulo / vazio, mas a abordagem da lista reterá a e b (com o (a+b) remover falhando em encontrar nada)

Outras dicas

Usar um multicastDelegate para manter uma lista de eventos inscritos é certamente uma abordagem viável, mas não é particularmente interessada. Para adicionar ou remover um evento de um multicastDelegate, é preciso fazer uma de duas coisas:

  1. Adquira um bloqueio, crie um novo delegado a partir do antigo, mas com um evento adicionado ou removido, defina o ponteiro delegado para esse delegado e depois solte o bloqueio
  2. Copie uma referência ao delegado antigo, crie um novo delegado a partir dele, mas com um evento adicionado ou removido, use interlocked. .

A última abordagem pode oferecer um desempenho um pouco melhor se não houver contenção, mas pode ter um desempenho ruim se muitos threads estiverem simultaneamente tentando adicionar ou remover eventos. Uma vantagem dessa última abordagem, no entanto, é que não há perigo de um fio morrer enquanto segura uma fechadura.

Nenhuma das abordagens parece particularmente limpa. Se alguém está planejando invocar todos os eventos, simplesmente invocando um delegado, o desempenho da entrada de eventos poderá compensar o desempenho Adicionar/Remover. Por outro lado, se alguém planeja usar o GetInvocationList, de modo a embrulhar chamadas de eventos nos blocos de tentativa/captura, pode -se ser melhor usando uma lista (adequadamente bloqueada) ou outra estrutura de dados.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top