Lascia che lo metta per primo e ritorni ad esso:
A WeakReference is useful when you want to keep tabs on an object, but you DO NOT want your observations to prevent that object from being collected
Quindi iniziamo dall'inizio:
- mi scuso 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
- specifichiamolo come un'istanza di class Foo
- NON PUO 'viverlo da solo (per lo più vero); Nello stesso modo in cui "Nessun uomo è un'isola", ci sono solo alcuni modi in cui un oggetto può essere promosso a Islandhood - sebbene si chiami essere una radice di GC nel linguaggio CLR. Essendo una radice di GC, o avendo una catena stabilita di connessioni / riferimenti a una radice di GC, è fondamentalmente ciò che determina se Foo x = new Foo()
ottiene o meno la garbage collection.
Se non riesci a risalire verso la radice di un GC per heap o stack walking, sei effettivamente orfano e probabilmente verrà contrassegnato / raccolto il prossimo ciclo.
A questo punto, esaminiamo alcuni esempi orribili:
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--);
}
}
Semplicemente semplice - non è thread-safe, quindi non provarlo, ma mantiene un "conteggio dei riferimenti" approssimativo delle istanze attive e dei decrementi 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à una root GC di sua proprietà (beh ... per essere precisi, verrà radicata tramite una catena direttamente al dominio dell'app che esegue questa applicazione, ma questo è un altro argomento) ha due metodi di aggancio su un'istanza Foo
: testiamolo:
// 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-che-è-stato-riferito-a-da f
sia "collezionabile"?
No, perché ora c'è un altro oggetto con un riferimento ad esso - il Dictionary
in quell'istanza di Singleton
static.
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 eseguiamo il nostro riferimento a %-Foo
-that-was-once- f
, non ci sono più riferimenti "duri" all'oggetto, quindi è collettabile - il WeakReference
creato da l'ascoltatore debole non lo impedirà.
Buoni casi d'uso:
-
Gestori di eventi (Anche se leggi prima questo: Eventi deboli in C # )
-
Hai una situazione in cui potresti causare un "riferimento ricorsivo" (ad esempio, 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)
-
Vuoi "trasmettere" qualcosa a una collezione di oggetti, ma non vuoi essere la cosa che li mantiene in vita; un List<WeakReference>
può essere mantenuto facilmente, e persino potato eliminando dove ref.Target == null