2009-06-05 13 views
7

Diciamo che ho una classe A che può sparare un evento chiamato X. Ora ho una classe B e in un metodo ottengo un esempio per A e legare l'evento a un gestore in B:Come sbarazzarsi degli handler di eventi in modo sicuro?

public void BindEvent(A a) 
{ 
    a.X += AEventHandler; 
} 

Ho tre domande a riguardo.

  • È vero che quando ora ho impostato il riferimento all'istanza B NULL, non sarà garbage collection poiché il garbage collector pensa che sia ancora in uso (mantenendo così un inutile e potenzialmente interferenti copia B in memoria).

  • Che dire quando ho un altro oggetto c (di classe C) in cui ho un riferimento a A chiamato ("this.a = new A()"). Quindi chiamo "b.BindEvent (this.a)", e in c ho impostato il riferimento a a a null ("this.a = null"). Questo manterrà la copia di A in memoria perché è referenziata attraverso l'evento in b?

  • Se uno o entrambi sono vere di quanto sopra, come posso aggirare meglio questi problemi? Se ho un intero elenco di gestori di eventi (diciamo 10 righe come "a.SomeEvent + = SomeMethod") dovrei ripulirli tutti di nuovo ("a.SomeEvent - = SomeMethod"). In quale momento o luogo nel codice dovrei fare queste cose?

Beh, è ​​diventato un po 'confuso ma non sono sicuro di come spiegare in un modo migliore. Per favore lascia un commento se ho bisogno di spiegare qualcosa di più dettagliato.

risposta

8

così: A è l'editore e B è l'abbonato?

primo proiettile: se B è l'istanza con AEventHandler - allora è ancora in uso, quindi no, non otterrà raccolte a meno che l'istanza a non è raggiungibile.

secondo punto: eh? (leggerà di nuovo ...) Se le istanze A e B non sono raggiungibili, saranno raccolte automaticamente; l'evento non ha importanza. Se A è raggiungibile, allora B rimarrà attivo. Tuttavia, l'abbonamento all'evento mai mantiene A attivo; è un modo ... A può mantenere vivo B, ma B non mantiene A vivo. Questo lo copre?

terzo punto: nella maggior parte dei casi, le due cose hanno un'espansione di vita simile, quindi non è un problema. Diventa solo un problema se la cosa che pubblica l'evento vive molto più a lungo delle cose con i gestori. In tal caso, devi semplicemente pulire religiosamente te stesso, ad esempio: a.X -= AEventHandler. In particolare, gli eventi static sono malvagi per questo motivo.

4

È consigliabile non associare il gestore eventi prima di distruggere l'istanza della classe a cui si riferisce. (Usando il codice come un exmaple.)

public void UnbindEvent(A a) 
{ 
    a.X -= AEventHandler; 
} 

vorrei anche chiedere, perché stai impostando le variabili di classe a null?

2
  1. Corretto.
  2. Corretto.
  3. Perché vorresti evitare questo comportamento? È come il GC è progettato per funzionare. Se hai bisogno di fare un po 'di pulizia quando ogni oggetto è abbattuto, usa il modello Dispose.
+0

Sono abbastanza sicuro che 2 non è corretto. Innanzitutto, l'evento * mai * mantiene vivo l'editore ('A') - e in secondo luogo, nulla può vedere' A' comunque ... quindi i dati vengono disconnessi e raccolti. –

+0

Doh! Sono corretto. :-) –

1

Sì, l'evento è un riferimento, se non si annulla la registrazione, l'oggetto che implementa il gestore eventi non sarà sottoposto a Garbage Collection.

Si può fare questo:

Rimuovere tutti gli eventi registrati, se A sa quando non sono più utilizzati.

class A 
{ 
    // clearing all registrations 
    private void ClearEvents() 
    { 
    X = null; 
    } 
} 

Oppure si annulla la registrazione in B, se B sa quando non lo usa più. È necessario mantenere un riferimento a per poter annullare la registrazione.

È inoltre possibile implementare IDisposable.

class B : IDisposable 
{ 
    private A registeredToA; 

    public void BindEvent(A a) 
    { 
    registeredToA = a; 
    registeredToA.X += AEventHandler; 
    } 

    public void Dispose() 
    { 
    registeredToA.x -= AEventHandler; 
    } 
} 

Questa è una modifica al codice, poiché B deve essere sempre eliminato.

Problemi correlati