Pozwól, że postawię to na pierwszym miejscu i wrócę do tego:
WeakReference jest przydatny, gdy chcesz mieć oko na obiekt, ale NIE chcesz, aby twoje obserwacje zapobiegały gromadzeniu tego obiektu
Zacznijmy od początku:
- przepraszam z góry za niezamierzone przestępstwo, ale wrócę na chwilę do poziomu „Dicka i Jane”, ponieważ nigdy nie można powiedzieć publiczności.
Więc kiedy masz obiekt X
- określmy go jako instancję class Foo
- NIE MOŻE on żyć sam (głównie prawda); W ten sam sposób, w jaki „Żaden człowiek nie jest wyspą”, istnieje tylko kilka sposobów, w jakie obiekt może awansować do Islandhood - chociaż nazywa się to korzeniem GC w CLR. Bycie rootem GC lub posiadanie ustalonego łańcucha połączeń / odniesień do roota GC jest w zasadzie tym, co decyduje o tym, czy Foo x = new Foo()
śmieci są zbierane, czy nie .
Jeśli nie możesz wrócić do korzenia GC ani przez stertę, ani przez stos, jesteś skutecznie osierocony i prawdopodobnie zostaniesz oznaczony / zebrany w następnym cyklu.
W tym miejscu spójrzmy na kilka okropnie wymyślonych przykładów:
Po pierwsze 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--);
}
}
Dość proste - nie jest bezpieczne dla wątków, więc nie próbuj tego, ale zachowuje przybliżoną „liczbę referencji” aktywnych instancji i dekrecji po ich sfinalizowaniu.
Teraz spójrzmy na 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");
}
}
Mamy więc obiekt, który jest już własnym katalogiem głównym GC (cóż ... konkretnie, zostanie zrootowany przez łańcuch bezpośrednio do domeny aplikacji, w której działa ta aplikacja, ale to inny temat), który ma dwie metody zaczepienia się na Foo
instancji - przetestujmy to:
// 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();
Czy w związku z powyższym spodziewałbyś się, że obiekt, o którym kiedyś była mowa, f
będzie „kolekcjonerski”?
Nie, ponieważ istnieje inny obiekt, który teraz zawiera odniesienie do niego - Dictionary
w tej Singleton
instancji statycznej.
Ok, spróbujmy słabego podejścia:
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();
Teraz, kiedy mamy walnięcie nasze odniesienie do dokonywanych poza rynkiem Foo
-to-było-raz w miesiącu f
, nie nie są bardziej „twarde” odniesienia do obiektu, więc jest to dla kolekcjonerów - w WeakReference
stworzony przez słabego słuchacza nie będzie temu zapobiec.
Dobre przypadki użycia:
Procedury obsługi zdarzeń (chociaż najpierw przeczytaj: Słabe zdarzenia w C # )
Masz sytuację, w której spowodowałbyś „odwołanie rekurencyjne” (tj. Obiekt A odnosi się do obiektu B, który odnosi się do obiektu A, zwanego również „przeciekiem pamięci”) (edytuj: derp, oczywiście, że to nie jest to prawda)
Chcesz „nadawać” coś do zbioru obiektów, ale nie chcesz być tym, co utrzymuje je przy życiu; a List<WeakReference>
można łatwo utrzymać, a nawet przyciąć, usuwając gdzieref.Target == null