Lassen Sie mich dies zuerst ausdrücken und darauf zurückkommen:
Eine WeakReference ist nützlich, wenn Sie ein Objekt im Auge behalten möchten, aber NICHT möchten, dass Ihre Beobachtungen verhindern, dass dieses Objekt gesammelt wird
Fangen wir also von vorne an:
Ich entschuldige mich im Voraus für jede unbeabsichtigte Beleidigung, aber ich werde für einen Moment wieder auf "Dick and Jane" -Niveau aufsteigen, da man es seinem Publikum niemals sagen kann.
Wenn Sie also ein Objekt haben X
- geben wir es als Instanz von an class Foo
-, kann es NICHT alleine leben (meistens wahr). Genauso wie "Kein Mensch ist eine Insel" gibt es nur wenige Möglichkeiten, wie ein Objekt zur Insel heraufgestuft werden kann - obwohl es in der CLR als GC-Root bezeichnet wird. Als GC-Root oder mit einer festgelegten Kette von Verbindungen / Verweisen auf eine GC-Root wird im Grunde genommen bestimmt, ob Foo x = new Foo()
Müll gesammelt wird oder nicht .
Wenn Sie nicht durch Haufen- oder Stapelgehen zu einer GC-Wurzel zurückkehren können, sind Sie effektiv verwaist und werden wahrscheinlich im nächsten Zyklus markiert / gesammelt.
Schauen wir uns an dieser Stelle einige schrecklich erfundene Beispiele an:
Erstens unser 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--);
}
}
Ziemlich einfach - es ist nicht threadsicher. Versuchen Sie es also nicht, sondern führen Sie eine grobe "Referenzzählung" der aktiven Instanzen und Dekremente durch, wenn sie abgeschlossen sind.
Schauen wir uns nun Folgendes an 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");
}
}
Wir haben also ein Objekt, das bereits ein eigenes GC-Stammverzeichnis hat (genauer gesagt, es wird über eine Kette direkt zu der App-Domäne gerootet, in der diese Anwendung ausgeführt wird, aber das ist ein anderes Thema), das über zwei Methoden verfügt Sich an eine Foo
Instanz zu klinken - lass es uns ausprobieren:
// 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();
Würden Sie nun von oben erwarten, dass das Objekt, auf das einmal Bezug genommen wurde f
, "sammelbar" ist?
Nein, denn es gibt ein anderes Objekt, das jetzt einen Verweis darauf enthält - das Dictionary
in dieser Singleton
statischen Instanz.
Ok, lass uns den schwachen Ansatz ausprobieren:
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();
Wenn wir nun unseren Verweis auf Foo
das, was einmal war, verwechseln f
, gibt es keine "harten" Verweise mehr auf das Objekt, so dass es sammelbar ist - das WeakReference
, was der schwache Zuhörer erschafft, wird das nicht verhindern.
Gute Anwendungsfälle:
Ereignishandler (obwohl zuerst gelesen: Schwache Ereignisse in C # )
Sie haben eine Situation, in der Sie eine "rekursive Referenz" verursachen würden (dh Objekt A bezieht sich auf Objekt B, das sich auf Objekt A bezieht, auch als "Memory Leak" bezeichnet) (edit: derp, natürlich ist dies nicht der Fall) nicht wahr)
Sie möchten etwas an eine Sammlung von Objekten "senden", aber Sie möchten nicht die Sache sein, die sie am Leben erhält. a List<WeakReference>
kann leicht gewartet und sogar entfernt werdenref.Target == null