Vorrei prima mettere questo fuori e tornare ad esso:
Un riferimento debole è utile quando si desidera tenere sotto controllo un oggetto, ma NON si desidera che le proprie osservazioni impediscano la raccolta di tale oggetto
Quindi partiamo dall'inizio:
- si scusa in anticipo per qualsiasi offesa involontaria, ma tornerò al livello "Dick and Jane" per un momento poiché non si può mai dire al proprio pubblico.
Quindi quando hai un oggetto X
- specificiamolo come istanza di class Foo
- NON PU CAN vivere da solo (per lo più vero); Allo stesso modo in cui "Nessun uomo è un'isola", ci sono solo alcuni modi in cui un oggetto può essere promosso a Islandità - sebbene si chiami essere una radice GC nel linguaggio CLR. Essere un GC Root o avere una catena consolidata di connessioni / riferimenti a una radice GC, è fondamentalmente ciò che determina se Foo x = new Foo()
raccogliere i rifiuti.
Se non riesci a risalire a qualche radice GC senza camminare in heap o stack, sei effettivamente orfano e verrai probabilmente contrassegnato / raccolto nel ciclo successivo.
A questo punto, diamo un'occhiata ad alcuni esempi orribili inventati:
Innanzitutto, il nostro Foo
:
public class Foo
{
private static volatile int _ref = 0;
public event EventHandler FooEvent;
public Foo()
{
_ref++;
Console.WriteLine("I am #{0}", _ref);
}
~Foo()
{
Console.WriteLine("#{0} dying!", _ref--);
}
}
Abbastanza semplice: non è thread-safe, quindi non provarlo, ma mantiene un "conteggio di riferimento" approssimativo di istanze e decrementi attivi quando vengono finalizzati.
Ora diamo un'occhiata a FooConsumer
:
public class NastySingleton
{
// Static member status is one way to "get promoted" to a GC root...
private static NastySingleton _instance = new NastySingleton();
public static NastySingleton Instance { get { return _instance;} }
// testing out "Hard references"
private Dictionary<Foo, int> _counter = new Dictionary<Foo,int>();
// testing out "Weak references"
private Dictionary<WeakReference, int> _weakCounter = new Dictionary<WeakReference,int>();
// Creates a strong link to Foo instance
public void ListenToThisFoo(Foo foo)
{
_counter[foo] = 0;
foo.FooEvent += (o, e) => _counter[foo]++;
}
// Creates a weak link to Foo instance
public void ListenToThisFooWeakly(Foo foo)
{
WeakReference fooRef = new WeakReference(foo);
_weakCounter[fooRef] = 0;
foo.FooEvent += (o, e) => _weakCounter[fooRef]++;
}
private void HandleEvent(object sender, EventArgs args, Foo originalfoo)
{
Console.WriteLine("Derp");
}
}
Quindi abbiamo un oggetto che è già un suo root GC (beh ... per essere precisi, sarà radicato tramite una catena direttamente al dominio dell'app che esegue questa applicazione, ma questo è un altro argomento) che ha due metodi di aggancio a Foo
un'istanza - proviamolo:
// Our foo
var f = new Foo();
// Create a "hard reference"
NastySingleton.Instance.ListenToThisFoo(f);
// Ok, we're done with this foo
f = null;
// Force collection of all orphaned objects
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Ora, da quanto sopra, ti aspetteresti che l'oggetto "a cui una volta veniva riferito f
" fosse "collezionabile"?
No, perché ora c'è un altro oggetto che contiene un riferimento ad esso - il Dictionary
in Singleton
quell'istanza statica.
Ok, proviamo l'approccio debole:
f = new Foo();
NastySingleton.Instance.ListenToThisFooWeakly(f);
// Ok, we're done with this foo
f = null;
// Force collection of all orphaned objects
// This should collect # 2 - you'll see a "#2 dying"
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Ora, quando cancelliamo il nostro riferimento a quello Foo
che era una volta f
, non ci sono più riferimenti "duri" all'oggetto, quindi è raccoglibile - il WeakReference
creato dall'ascoltatore debole non lo impedirà.
Buoni casi d'uso:
Gestori di eventi (anche se leggi prima: Eventi deboli in C # )
Hai una situazione in cui potresti causare un "riferimento ricorsivo" (ovvero, l'oggetto A si riferisce all'oggetto B, che si riferisce all'oggetto A, indicato anche come "Perdita di memoria") (modifica: derp, ovviamente questo non è è vero)
Volete "trasmettere" qualcosa a una collezione di oggetti, ma non volete essere la cosa che li tiene in vita; a List<WeakReference>
può essere mantenuto facilmente e persino eliminato rimuovendo doveref.Target == null