2009-04-21 12 views
16

Non sono sicuro di essere completamente chiaro sulle implicazioni del collegamento agli eventi negli oggetti.In quali casi è necessario staccare gli eventi?

Questa è la mia attuale comprensione, corretto o elaborato:

1. Collegamento ad eventi di livello locale non hanno bisogno di essere distaccato

Esempi:

this.Closing += new System.ComponentModel.CancelEventHandler(MainWindow_Closing);

public event EventHandler OnMyCustomEvent = delegate { };

Presumo che quando il tuo ob ject è disposto o garbage collection, le funzioni sono deallocate e si disconnetterebbero automaticamente dagli eventi.

2. Collegamento a oggetti non più necessari (= null;) devono essere separate da

Esempi: Allegare all'evento trascorso di un timer, che si risponde solo per una volta. Suppongo che sia necessario memorizzare il timer in una variabile locale in modo da poter staccare l'evento trascorso dopo l'attivazione dell'evento. Così, dichiarando il timer in un ambito metodo locale in questo modo si tradurrebbe in una perdita:

System.Timers.Timer myDataTimer = new System.Timers.Timer(1000); myDataTimer.Elapsed += new System.Timers.ElapsedEventHandler(myDataTimer_Elapsed);

3. Collegamento a eventi in un oggetto locale per la vostra classe non richiede lo smaltimento?

Ad esempio, se si dispone di una ObservableCollection che crea, monitora e lascia morire. Se ci si è collegati all'evento CollectionChanged utilizzando una funzione locale privata, questa funzione non sarebbe stata deallocata quando la classe è stata sottoposta a garbage collection, facendo sì che anche ObservableCollection venisse liberato?

Sono sicuro di avere punti in cui ho smesso di usare oggetti e non sono riuscito a staccarmi da un evento (ad esempio, l'esempio del timer che ho creato), quindi sto cercando una spiegazione più chiara su come funziona .

risposta

22

Penso che lo stai rendendo più complicato di quanto dovrebbe essere. Hai solo bisogno di ricordare due cose:

  • Quando si sottoscrive un evento, "proprietario" della manifestazione (l'editore) generalmente mantiene un riferimento al delegato ti iscrivi con.
  • Se si utilizza un metodo di istanza come azione di un delegato, il delegato ha un riferimento al suo oggetto "target".

Questo significa che se si scrive:

publisher.SomeEvent += subscriber.SomeMethod; 

Poi subscriber non sarà per la garbage collection prima publisher è a meno che non annullare l'iscrizione in seguito.

nota che in molti casi, è solo subscriberthis:

publisher.SomeEvent += myDataTimer_Elapsed; 

è equivalente a:

publisher.SomeEvent += this.myDataTimer_Elapsed; 

supponendo che è un metodo di istanza.

C'è no rapporto inverso solo a causa dell'iscrizione all'evento - in altre parole l'abbonato non mantiene l'editore in vita.

Vedere my article on events and delegates per ulteriori informazioni, a proposito.

+0

Hai perfettamente ragione, sto complicando le cose. Cercando degli esempi, quasi tutti hanno mostrato di staccarsi dall'evento, il che mi ha portato a credere che l'abbonato potesse mantenere vivo l'editore. –

+0

Domanda stupida: questo implica che se il publisher si iscrive a un evento nel Sottoscrittore, allora nessuno dei due può/sarà raccolto? –

+2

No, significa che c'è un riferimento circolare - non appena non ci sono * altri riferimenti * radicati a uno di essi, entrambi saranno idonei per GC. –

1

Il caso in questione in cui si deve annullare l'iscrizione a un evento è come questo:

public class A 
{ 
    // ... 
    public event EventHandler SomethingHappened; 
} 

public class B 
{ 
    private void DoSomething() { /* ... */ } // instance method 

    private void Attach(A obj) 
    { 
     obj.SomethingHappened += DoSomething(); 
    } 
} 

In questo scenario, quando si smaltisce un B, ci sarà ancora un riferimento penzoloni ad esso da obj 's gestore di eventi. Se si desidera recuperare la memoria di B, è necessario prima staccare B.DoSomething() dal gestore eventi pertinente.

si potrebbe incorrere nella stessa cosa se la linea di sottoscrizione di eventi si presentava così, naturalmente:

obj.SomethingHappened += someOtherObject.Whatever.DoSomething(); 

Ora è someOtherObject che è sul gancio e non può essere garbage collection.

3

I restanti riferimenti che impediscono la garbage collection hanno un ulteriore effetto che può essere ovvio ma non ancora dichiarato in questa discussione; anche il gestore eventi allegato sarà eccitato.

Ho sperimentato questo un paio di volte. Uno era quando avevamo un'applicazione che diventava gradualmente più lenta e più lenta man mano che correva. L'applicazione ha creato l'interfaccia utente in modo dinamico caricando i controlli utente. Il contenitore ha consentito ai controlli dell'utente di sottoscrivere determinati eventi nell'ambiente e uno di questi non è stato annullato quando i controlli sono stati "scaricati".

Dopo un po 'questo ha portato a un numero elevato di listener di eventi che venivano eseguiti ogni volta che veniva sollevato un particolare evento. Ciò può ovviamente portare a condizioni di gara gravi quando un buon numero di istanze "dormienti" si sveglia improvvisamente e tenta di agire sullo stesso input.

In breve; se scrivi codice per collegare un listener di eventi; assicurati di essere rilasciato non appena non è più necessario. Ho quasi il coraggio di prometterti che ti salverà da almeno un mal di testa ad un certo punto nel futuro.

Problemi correlati