2010-01-05 9 views
14

Diciamo che abbiamo 2 oggetti, Broadcaster e Listener. Il broadcaster ha un evento chiamato Broadcast a cui è iscritto l'ascoltatore. Se Listener viene eliminato senza annullamento dell'iscrizione dall'evento Broadcast, verrà conservato in memoria a causa dell'evento delegato che fa riferimento a tale broadcast.Gli eventi personalizzati devono essere impostati su null quando si smaltisce un oggetto?

Ciò di cui sono curioso è se Broadcaster è disposto senza l'annullamento dell'iscrizione di Listener o l'impostazione di Broadcast Broadcast = null Broadcaster verrà conservato in memoria?

Non sono stato in grado di localizzare nulla con una risposta difficile a questa domanda tranne un blogger che crede che non impostando l'evento su null manterrà la fonte in memoria (trovato here).

Mi piacerebbe sentire una spiegazione del perché o perché no.

Grazie.

UPDATE: Forum Thread where one developer indicates events should be set to null, but Jon Skeet indicates it's not necessary, but doesn't elaborate.

+0

Immagino tu voglia dire "l'ascoltatore verrà conservato in memoria?" –

+0

Io no. Mi riferisco a Broadcaster. La domanda riguarda una fonte di eventi che viene eliminata mentre un utente è ancora in vita. –

+0

(risposto al nuovo commento) –

risposta

9

Nota che i delegati non mantengono viva l'editore (tengono solo il target = abbonato vivo), in modo che nessun numero di abbonamenti saranno (da soli) mantenere vivo l'emittente. In quanto tale, da questa prospettiva non importa se è disposto o meno. Quando non ci sono articoli che fanno riferimento all'emittente (e le sottoscrizioni agli eventi non contano per questo), sarà idoneo per la raccolta.

Essenzialmente, un delegato è un (elenco) coppia (s) di MethodInfo e object riferimenti; il metodo da chiamare e l'oggetto da richiamare come "arg0" (alias this). Semplicemente non ha un riferimento all'oggetto che aumenta l'evento.

Ecco le prove che un ascoltatore non mantiene la sorgente viva; dovresti vedere che "Sorgente 1" viene raccolta, anche se abbiamo ancora il listener corrispondente che è iscritto. Come previsto, "Listener 2" fa non ottenere raccolti, dal momento che abbiamo ancora l'emittente corrispondente:

class DataSource 
{ 
    public DataSource(string name) { this.name = name; } 
    private readonly string name; 
    ~DataSource() { Console.WriteLine("Collected: " + name); } 

    public event EventHandler SomeEvent; 
} 
class DataListener 
{ 
    public DataListener(string name) { this.name = name; } 
    private readonly string name; 
    ~DataListener() { Console.WriteLine("Collected: " + name); } 
    public void Subscribe(DataSource source) 
    { 
     source.SomeEvent += SomeMethodOnThisObject; 
    } 
    private void SomeMethodOnThisObject(object sender, EventArgs args) { } 
} 

static class Program 
{ 
    static void Main() 
    { 
     DataSource source1 = new DataSource("Source 1"), 
       source2 = new DataSource("Source 2"); 
     DataListener listener1 = new DataListener("Listener 1"), 
       listener2 = new DataListener("Listener 2"); 
     listener1.Subscribe(source1); 
     listener2.Subscribe(source2); 
     // now we'll release one source and one listener, and force a collect 
     source1 = null; 
     listener2 = null; 
     GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); 
     GC.WaitForPendingFinalizers(); // source 1 gets collected, ONLY 

     Console.WriteLine("Done"); 
     Console.ReadLine(); 
     GC.KeepAlive(source2); // prevents collection due to optimisation 
     GC.KeepAlive(listener1); // prevents collection due to optimisation 
    } 
} 
+0

Sì, ma potrei porre la risposta che MethodInfo fa riferimento al metodo per chiamare quale è sull'abbonato creando quindi un riferimento tra il broadcaster e il subscriber. La spiegazione potrebbe essere nella direzione del riferimento (Broadcaster -> Subscriber, non il contrario). –

+0

@Dan - Assolutamente, è ** in direzione dell'emittente dell'abbonato, che è esattamente il motivo per cui il riferimento non mantiene in vita l'emittente. –

+0

In casi semplici, non importa se un editore di eventi annulla la sua lista di abbonati su 'Dispose'. D'altra parte, se qualcosa mantiene un riferimento rooted inutile a un editore di eventi dopo che è stato eliminato, qualsiasi elenco di sottoscrittori che non viene annullato diventerà riferimenti rooting inutili a tali sottoscrittori. Le liste di abbonati nuoter su dispose possono trasformare quelle che sarebbero grandi perdite di memoria in quelle piccole. A volte può essere una buona cosa (se si trasforma una perdita di memoria che uccide un programma dopo 45 minuti in uno che lo uccide dopo 45 giorni, e il programma deve solo funzionare per 60 minuti). – supercat

4

No. L'obiettivo delegato in caso di trasmissione fa riferimento all'oggetto listener. Ciò manterrà vivo l'oggetto Listener. L'oggetto Listener non ha alcun riferimento all'oggetto Broadcast.

Attenzione per la terminologia. Smaltire l'oggetto Broadcast non fa nulla. Deve essere garbage collection che può accadere solo quando non ci sono riferimenti all'oggetto. Quando ciò accade, l'oggetto delegato verrà automaticamente raccolto anche dal momento che l'unico riferimento ad esso è l'elenco interno di destinazioni delegate gestite da oggetto delegato evento privato. Ciò rimuove anche il riferimento che il delegato ha all'ascoltatore. Se non ci sono altri riferimenti all'ascoltatore, verrà raccolto anche. Se lo è ancora, non riceverà più le notifiche degli eventi. Per farla breve: non è necessario impostare in modo esplicito l'evento su null nella classe Broadcast.

Non proprio uguale nell'ascoltatore, fa riferimento all'evento a cui si è abbonato. Se è dichiarato non idoneo per le attività commerciali (disponibile) ma l'emittente è ancora in diretta, deve rimuovere esplicitamente l'iscrizione all'evento. La classe SystemEvents è una versione estrema di questo, i suoi eventi sono statici. Gli eventi di attivazione su un delegato che fa riferimento a un listener disposto sono qualcosa che si tende a notare.

La maggior parte dei modelli di oggetti pratici provano a far sparire gli oggetti ascoltatori quando il genitore va. Windows Forms sarebbe un buon esempio. Non è quindi necessario cancellare in modo esplicito gli eventi.

+0

In realtà, è possibile impostare un evento su null all'interno della classe che lo dichiara. –

+0

Cosa intendi per "Qual è qualcosa che la classe Broadcast non può fare, non può generare un'istanza delegata per annullare l'iscrizione."? –

+0

Hai ragione, ho ottimizzato il post. –

Problemi correlati