2011-03-04 16 views
5

Se creo una classe .NET che sottoscrive un evento con una funzione anonima come questa:Devo rimuovere questo tipo di gestore eventi?

void MyMethod() 
{ 
    Application.Current.Deactivated += (s,e) => { ChangeAppearance(); }; 
} 

Sarà questo gestore di eventi è una radice mantenere la mia classe di essere garbage collection?

In caso contrario, wohoo! Ma se è così, puoi mostrarmi la sintassi di rimozione? Usare semplicemente - = con lo stesso codice sembra sbagliato.

risposta

3

È possibile utilizzare un metodo di "reale" come suggerisce Simon D., o fare questo:

EventHandler handler = null; 
handler = (s,e) => { 
    Application.Current.Deactivated -= handler; 
    ChangeAppearance(); 
}; 
Application.Current.Deactivated += handler; 

Dal momento che questo è un po 'brutto e sconfigge lo scopo di utilizzare un lambda di essere succinta, I 'd probabilmente andare con il refactoring su un metodo. Ma è utile sapere cos'altro funziona.

Attenzione: E 'ovvio che bisogna essere super ultra attenzione a non pasticciare con il valore di handler a tutti tra il punto in cui si sottoscrive l'evento e il punto in cui si è effettivamente invocato, altrimenti la annullare la sottoscrizione della parte non funzionerà correttamente.

+0

Mi piace lo stile di succinta qui. Per i miei bisogni, questo dovrebbe funzionare perfettamente. Grazie per aver dedicato del tempo per scrivere questo! – jschroedl

3

Penso che sia necessario annullare l'iscrizione, perché il fornitore di eventi (l'applicazione) vive - o almeno può vivere - più a lungo del proprio consumatore. Quindi ogni istanza di iscrizione che muore mentre l'app rimane in vita creerà perdite di memoria.

Stai abbonando un delegato anonimo all'evento. Questo è il motivo per cui non è possibile cancellarlo allo stesso modo, perché non è più possibile indirizzarlo. In realtà si sta creando il metodo nello stesso momento in cui si abbona e non si memorizza alcun puntatore al metodo appena creato.

Se si cambia un po 'l'implementazione di utilizzare un metodo di "reale", si può facilmente annullare l'iscrizione a caso nello stesso modo si è abbonati ad esso:

Application.Current.Deactivated += ChangeAppearance; 
Application.Current.Deactivated -= ChangeAppearance; 
private void ChangeAppearance(object sender, EventArgs eventArgs) 
{ 
    throw new NotImplementedException(); 
} 
+0

Grazie per aver confermato il sospetto. -John – jschroedl

1

Questa è davvero una perdita che impedisce la raccolta dei rifiuti. Ci sono modi per aggirarlo, con WeakReference che è uno dei migliori.

This link ha una buona conversazione e buone risposte per voi.

+0

Grazie per il puntatore. Quei ragazzi si divertono molto in questo !! – jschroedl

2

È assolutamente necessario pulire il riferimento. Se ci fossero dubbi, potresti facilmente testare il tuo evento statico in questo modo.

static class MemoryLeak 
{ 
    static List<Action<int>> list = new List<Action<int>>(); 
    public static event Action<int> ActivateLeak 
    { 
     add 
     { 
      list.Add(value); 
     } 
     remove 
     { 
      list.Remove(value); 
     } 
    } 
} 

Quindi, impostando un punto di interruzione nella funzione di rimozione si può vedere che il riferimento non è pulito.

class Program 
{ 
    static void Main(string[] args) 
    { 
     foo f = new foo(); 
     MemoryLeak.ActivateLeak += o => f.bar(); 
     f.tryCleanup(); 
    } 
} 

class foo 
{ 
    public void bar() 
    { } 

    public void tryCleanup() 
    { 
     MemoryLeak.ActivateLeak -= o => bar(); 
    } 
} 

Come alternativa alla soluzione di Simon si potrebbe usare una seconda chiusura per creare un'azione di "staccare", che può essere passato in giro.

foo f = new foo(); 
Action<int> callfoo = o => f.bar(); 
MemoryLeak.ActivateLeak += callfoo; 
Action cleanUp =() => MemoryLeak.ActivateLeak -= callfoo; 

// Now you can pass around the cleanUp action and call it when you need to unsubscribe from the event. 
cleanUp(); 
Problemi correlati