È già stato aggiunto un gestore eventi?


183

C'è un modo per dire se un gestore eventi è stato aggiunto a un oggetto? Sto serializzando un elenco di oggetti in / fuori dallo stato della sessione in modo da poter utilizzare lo stato della sessione basato su SQL ... Quando un oggetto nell'elenco ha una proprietà modificata, deve essere contrassegnato, che il gestore di eventi ha gestito correttamente prima . Tuttavia ora quando gli oggetti vengono deserializzati non ottiene il gestore eventi.

In un impeto di lieve fastidio, ho appena aggiunto il gestore eventi alla proprietà Get che accede all'oggetto. Viene chiamato ora, il che è fantastico, tranne per il fatto che viene chiamato come 5 volte, quindi penso che il gestore continui ad essere aggiunto ogni volta che si accede all'oggetto.

È abbastanza sicuro da ignorare, ma preferirei renderlo molto più pulito controllando se il gestore è già stato aggiunto, quindi lo faccio solo una volta.

È possibile?

EDIT: Non ho necessariamente il pieno controllo di quali gestori di eventi vengono aggiunti, quindi il solo controllo di null non è abbastanza buono.


Risposte:


123

Al di fuori della classe che definisce, come menziona @Telos, puoi usare EventHandler solo sul lato sinistro di a +=o a -=. Pertanto, se si ha la possibilità di modificare la classe di definizione, è possibile fornire un metodo per eseguire il controllo controllando se il gestore eventi è null- in tal caso, non è stato aggiunto alcun gestore eventi. In caso contrario, forse e puoi scorrere i valori in Delegate.GetInvocationList . Se uno è uguale al delegato che desideri aggiungere come gestore eventi, sai che è 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 questo potrebbe essere facilmente modificato per diventare "aggiungi il gestore se non è lì". Se non hai accesso alle viscere della classe che espone l'evento, potrebbe essere necessario esplorare -=e +=, come suggerito da @Lou Franco.

Tuttavia, potresti essere meglio riesaminare il modo in cui stai commissionando e smantellando questi oggetti, per vedere se non riesci a trovare un modo per tracciare queste informazioni tu stesso.


7
Questo non viene compilato, EventHandler può essere solo sul lato sinistro di + = o - =.
CodeRedick,

2
Rimosso il voto discendente su ulteriori spiegazioni. Lo stato SQL sta praticamente distruggendo l'intera idea qui ... :(
CodeRedick il

1
Grazie Blair e SO cerca, proprio quello che stavo cercando (fastidioso che tu non possa farlo al di fuori della classe)
George Mauer

3
Il problema si verifica il più delle volte mentre si confrontano i delegati per l'uguaglianza. Quindi utilizzare Delegate.Equals(objA, objB)se si desidera verificare l'esistenza esattamente dello stesso delegato. Altrimenti confronta le proprietà singolarmente come if(objA.Method.Name == objB.Method.Name && objA.Target.GetType().FullName == objB.Target.GetType().FullName).
Sanjay,

2
Questo codice non funziona in un WinForm. È rigorosamente per ASP.NET?
jp2code

213

Recentemente sono arrivato a una situazione simile in cui dovevo registrare un gestore per un evento solo una volta. Ho scoperto che puoi prima annullare la registrazione in modo sicuro e quindi registrarti di nuovo, anche se il gestore non è registrato affatto:

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

Notare che ogni volta che si registra il gestore, si assicurerà che il gestore sia registrato una sola volta. Mi sembra una buona pratica :)


9
Sembra rischioso; se un evento viene generato dopo aver rimosso il gestore e prima di aggiungerlo nuovamente, verrà perso.
Jimmy,

27
Sicuro. Vuoi dire che non è thread-safe. Ma questo può essere solo un problema quando si eseguono più thread o simili, il che non è normale. Nella maggior parte dei casi questo dovrebbe essere abbastanza buono per motivi di semplicità.
alf

7
Questo mi dà fastidio. Solo perché al momento non stai creando esplicitamente thread nel tuo codice, ciò non significa che non ci siano più thread o che non verranno aggiunti in seguito. Non appena tu (o qualcun altro nel team, possibilmente mesi dopo) aggiungi un thread di lavoro o rispondi sia all'interfaccia utente che alla connessione di rete, questo apre la porta a eventi rilasciati altamente intermittenti.
Tecnophile

1
Credo che l'intervallo di tempo tra la rimozione e l'aggiunta di nuovo sia così piccolo, che è molto improbabile che esistano eventi persi, ma sì, è ancora possibile.
Alisson,

2
Se lo stai usando per qualcosa come l'aggiornamento di un'interfaccia utente ecc., Allora è un compito così banale che il rischio è ok. Se questo fosse per la gestione dei pacchetti di rete, ecc., Non lo userei.
lancia il

18

Se questo è l'unico gestore, puoi verificare se l'evento è nullo, in caso contrario il gestore è stato aggiunto.

Penso che puoi tranquillamente chiamare - = sull'evento con il tuo gestore anche se non viene aggiunto (in caso contrario, potresti prenderlo) - per assicurarti che non sia presente prima di aggiungere.


3
Questa logica si interromperà non appena l'evento verrà gestito anche da qualche altra parte.
Bugged87

6

Questo esempio mostra come utilizzare il metodo GetInvocationList () per recuperare i delegati a tutti i gestori che sono stati aggiunti. Se stai cercando di vedere se è stato aggiunto un gestore (funzione) specifico, puoi usare l'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

È possibile esaminare varie proprietà della proprietà Method del delegato per verificare se è stata aggiunta una funzione specifica.

Se stai cercando di vedere se ce n'è solo uno attaccato, puoi semplicemente provare a null.


GetInvocationList () non è un membro della mia classe. In effetti, non riesco a trovare questo metodo su nessun oggetto o gestore a cui ho accesso ...
CodeRedick,

Ci avevo provato anche io, a quanto pare puoi accedere all'evento in quel modo solo dall'interno della classe. Lo sto facendo genericamente, e come altri hanno già detto che i gestori di eventi probabilmente si stanno perdendo comunque. Grazie per il chiarimento!
CodeRedick,

4

Se capisco correttamente il tuo problema potresti avere problemi più grandi. Hai detto che altri oggetti possono iscriversi a questi eventi. Quando l'oggetto viene serializzato e deserializzato, gli altri oggetti (quelli di cui non si ha il controllo) perderanno i gestori di eventi.

Se non ti preoccupi, mantenere un riferimento al gestore dell'evento dovrebbe essere abbastanza buono. Se sei preoccupato per gli effetti collaterali di altri oggetti che perdono i gestori di eventi, allora potresti voler ripensare la tua strategia di memorizzazione nella cache.


1
D'oh! Non ci avevo nemmeno pensato ... anche se avrebbe dovuto essere ovvio considerando che il mio problema originale era che il mio gestore si perdeva.
CodeRedick,

2

sono d'accordo con la risposta di alf, ma poche modifiche ad esso sono, da usare,

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

2

L'unico modo che ha funzionato per me è creare una variabile booleana che ho impostato su true quando aggiungo l'evento. Quindi chiedo: se la variabile è falsa, aggiungo l'evento.

bool alreadyAdded = false;

Questa variabile può essere globale.

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

0
EventHandler.GetInvocationList().Length > 0

2
questo non viene lanciato quando l'elenco == null?
Boris Callens,

2
al di fuori della classe proprietaria del gestore eventi è possibile utilizzare solo - = e + =. non puoi accedere all'evento.
tbergelt,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.