Дозвольте мені викласти це першим і повернутися до нього:
WeakReference корисний, коли ви хочете вести вкладки на об’єкті, але ви НЕ хочете, щоб ваші спостереження запобігали збиранню цього об'єкта
Тож почнемо з початку:
- Заздалегідь вибачте за будь-яке ненавмисне правопорушення, але я на хвилину повернусь до рівня "Діка і Джейн", оскільки ніколи не можна сказати цій аудиторії.
Отже, коли у вас є об'єкт X
- давайте вказати його як екземпляр class Foo
- він НЕ МОЖЕ жити самостійно (здебільшого вірно); Так само, як "Жодна людина не острів", існує лише декілька способів, які об'єкт може просунути до Islandhood - хоча це називається коренем GC в CLR говорять. Бути коренем GC або мати налагоджену ланцюг зв’язків / посилань на корінь GC, в основному визначає, чи Foo x = new Foo()
збирається сміття чи ні .
Якщо ви не можете пройти шлях до коріння GC ні купою, ні стеком, ви фактично осиротіли і, ймовірно, буде позначений / зібраний наступним циклом.
На даний момент давайте розглянемо кілька жахливих прикладів:
По-перше, наші 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--);
}
}
Досить просто - це не безпечно для потоків, тому не намагайтеся цього робити, але зберігайте приблизний "підрахунок" активних екземплярів та декрементів при їх завершенні.
Тепер давайте розглянемо 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");
}
}
Таким чином, у нас є об'єкт, який вже є власним коренем GC (ну ... якщо бути конкретним, він буде вкорінюватися через ланцюг прямо до домену додатка, на якому працює ця програма, але це інша тема), що має два способи замикання на Foo
екземпляр - давайте перевіримо:
// 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();
Тепер, виходячи з вищесказаного, чи очікуєте ви, що об'єкт, про який було сказано колись, f
буде "збиральним"?
Ні, тому що є інший об'єкт, який зараз посилається на нього, - Dictionary
у цьому Singleton
статичному екземплярі.
Гаразд, спробуємо слабкий підхід:
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();
Тепер, коли ми збиваємо своє посилання на те- Foo
що-коли-було-коли- f
, більше немає "жорстких" посилань на об'єкт, тому він є колекціонуючим - WeakReference
створене слабким слухачем цього не завадить.
Гарні випадки використання:
Обробники подій (хоча прочитайте це перше: Слабкі події в C # )
У вас виникла ситуація, коли ви викликали б "рекурсивну посилання" (тобто об'єкт A посилається на об'єкт B, який посилається на об'єкт A, який також називають "витік пам'яті") (редагувати: derp, звичайно це не так неправда)
Ви хочете щось "транслювати" колекції предметів, але ви не хочете, щоб ця річ підтримувала їх живими; List<WeakReference>
може підтримуватися легко, і навіть обрізають, видаляючи , деref.Target == null