Um manipulador de eventos já foi adicionado?


183

Existe uma maneira de saber se um manipulador de eventos foi adicionado a um objeto? Estou serializando uma lista de objetos para dentro / fora do estado da sessão, para que possamos usar o estado da sessão com base em SQL ... Quando um objeto na lista tem uma propriedade alterada, ele precisa ser sinalizado, que o manipulador de eventos cuidou corretamente antes . No entanto, agora, quando os objetos são desserializados, ele não está recebendo o manipulador de eventos.

Em um incômodo leve, acabei de adicionar o manipulador de eventos à propriedade Get que acessa o objeto. Ele está sendo chamado agora, o que é ótimo, exceto pelo fato de ser chamado 5 vezes, então eu acho que o manipulador continua sendo adicionado sempre que o objeto é acessado.

É realmente seguro o suficiente para apenas ignorar, mas eu prefiro torná-lo muito mais limpo, verificando se o manipulador já foi adicionado, então eu o faço apenas uma vez.

Isso é possível?

Edição: Eu não tenho necessariamente controle total de quais manipuladores de eventos são adicionados, portanto, apenas verificar nulo não é bom o suficiente.


Respostas:


123

Fora da classe de definição, como o @Telos menciona, você só pode usar o EventHandler no lado esquerdo de um +=ou de um -=. Portanto, se você puder modificar a classe de definição, poderá fornecer um método para executar a verificação, verificando se o manipulador de eventos é null- nesse caso, nenhum manipulador de eventos foi adicionado. Caso contrário, talvez e você possa percorrer os valores em Delegate.GetInvocationList . Se um for igual ao delegado que você deseja adicionar como manipulador de eventos, você saberá que está lá.

public bool IsEventHandlerRegistered(Delegate prospectiveHandler)
{   
    if ( this.EventHandler != null )
    {
        foreach ( Delegate existingHandler in this.EventHandler.GetInvocationList() )
        {
            if ( existingHandler == prospectiveHandler )
            {
                return true;
            }
        }
    }
    return false;
}

E isso pode ser facilmente modificado para se tornar "adicione o manipulador, se não estiver lá". Se você não tem acesso às entranhas da classe que está expondo o evento, pode ser necessário explorar -=e +=, conforme sugerido por @Lou Franco.

No entanto, é melhor reexaminar a maneira como você comissiona e desativa esses objetos, para ver se você não consegue encontrar uma maneira de rastrear essas informações.


7
Isso não é compilado, o EventHandler pode estar apenas no lado esquerdo de + = ou - =.
CodeRedick 26/09/08

2
Removida a votação para mais explicações. O estado SQL está praticamente destruindo toda a ideia aqui ... :(
CodeRedick 26/09/08

1
Obrigado Blair e SO search, exatamente o que eu estava procurando (irritante você não pode fazê-lo fora da sala de aula) #
George Mauer

3
O problema ocorre na maioria das vezes ao comparar os delegados pela igualdade. Portanto, use Delegate.Equals(objA, objB)se você deseja verificar exatamente a mesma existência de delegado. Caso contrário, compare as propriedades individualmente if(objA.Method.Name == objB.Method.Name && objA.Target.GetType().FullName == objB.Target.GetType().FullName).
Sanjay

2
Este código não está funcionando em um WinForm. É estritamente para o ASP.NET?
Jp2code

213

Recentemente, cheguei a uma situação semelhante em que precisava registrar um manipulador para um evento apenas uma vez. Descobri que você pode cancelar o registro com segurança primeiro e depois se registrar novamente, mesmo se o manipulador não estiver registrado:

myClass.MyEvent -= MyHandler;
myClass.MyEvent += MyHandler;

Observe que fazer isso toda vez que você registrar seu manipulador garantirá que ele seja registrado apenas uma vez. Parece uma boa prática para mim :)


9
Parece arriscado; se um evento for disparado após remover o manipulador e antes de adicioná-lo novamente, ele será perdido.
Jimmy

27
Certo. Você quer dizer que não é thread thread safe. Mas isso só pode ser um problema ao executar vários threads ou algo semelhante, o que não é usual. Na maioria dos casos, isso deve ser bom o suficiente por uma questão de simplicidade.
alf

7
Isso me incomoda. Só porque você não está criando explicitamente tópicos no seu código, isso não significa que não haja vários tópicos ou que eles não serão adicionados posteriormente. Assim que você (ou alguém da equipe, possivelmente meses depois) adiciona um thread de trabalho ou responde à interface do usuário e à conexão de rede, isso abre a porta para eventos descartados altamente intermitentes.
Technophile

1
Acredito que a janela de tempo entre remover e adicionar novamente é tão pequena que é improvável que ocorram eventos perdidos, mas sim, ainda é possível.
Alisson

2
Se você estiver usando isso para algo como atualizar uma interface do usuário etc, então é uma tarefa tão trivial que o risco é aceitável. Se isso fosse para manipulação de pacotes de rede etc, então eu não o usaria.
rola

18

Se esse for o único manipulador, você poderá verificar se o evento é nulo; caso contrário, o manipulador foi adicionado.

Eu acho que você pode chamar com segurança - = no evento com seu manipulador, mesmo que não tenha sido adicionado (caso contrário, você pode pegá-lo) - para garantir que ele não esteja lá antes de adicionar.


3
Essa lógica será interrompida assim que o evento for tratado em outro lugar também.
bugged87

6

Este exemplo mostra como usar o método GetInvocationList () para recuperar delegados para todos os manipuladores que foram adicionados. Se você está olhando para ver se um manipulador específico (função) foi adicionado, você pode usar o array.

public class MyClass
{
  event Action MyEvent;
}

...

MyClass myClass = new MyClass();
myClass.MyEvent += SomeFunction;

...

Action[] handlers = myClass.MyEvent.GetInvocationList(); //this will be an array of 1 in this example

Console.WriteLine(handlers[0].Method.Name);//prints the name of the method

Você pode examinar várias propriedades na propriedade Method do delegado para verificar se uma função específica foi adicionada.

Se você estiver olhando para ver se há apenas um anexo, basta testar se há nulo.


GetInvocationList () não é um membro da minha classe. Na verdade, eu não consigo encontrar esse método em qualquer objeto ou manipulador Eu tenho acesso a ...
CodeRedick

Eu tentei isso também, aparentemente você só pode acessar o evento como esse de dentro da classe. Estou fazendo isso de maneira genérica e, como outros usuários mencionaram, os manipuladores de eventos provavelmente estão se perdendo de qualquer maneira. Obrigado pelo esclarecimento!
CodeRedick 26/09/08

4

Se entendi seu problema corretamente, você pode ter problemas maiores. Você disse que outros objetos podem se inscrever nesses eventos. Quando o objeto é serializado e desserializado, os outros objetos (aqueles sobre os quais você não tem controle) perderão seus manipuladores de eventos.

Se você não está preocupado com isso, manter uma referência ao seu manipulador de eventos deve ser bom o suficiente. Se você está preocupado com os efeitos colaterais de outros objetos perderem seus manipuladores de eventos, convém repensar sua estratégia de cache.


1
D'oh! Nem sequer tinha pensado nisso ... embora devesse ser óbvio, considerando que o meu problema original era o meu próprio manipulador se perder.
CodeRedick 26/09/08

2

eu concordo com a resposta do alf, mas pouca modificação é ,, usar,

           try
            {
                control_name.Click -= event_Click;
                main_browser.Document.Click += Document_Click;
            }
            catch(Exception exce)
            {
                main_browser.Document.Click += Document_Click;
            }

2

A única maneira que funcionou para mim é criar uma variável booleana que eu defini como true quando adiciono o evento. Então pergunto: se a variável for falsa, adiciono o evento.

bool alreadyAdded = false;

Essa variável pode ser global.

if(!alreadyAdded)
{
    myClass.MyEvent += MyHandler;
    alreadyAdded = true;
}

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.