Позвольте мне сначала высказать это и вернуться к этому:
Слабая ссылка полезна, когда вы хотите следить за объектом, но НЕ хотите, чтобы ваши наблюдения препятствовали сбору этого объекта.
Итак, начнем с самого начала:
- заранее извиняюсь за любое непреднамеренное оскорбление, но я ненадолго вернусь к уровню "Дик и Джейн", поскольку никто никогда не сможет рассказать об этом аудитории.
Поэтому, когда у вас есть объект 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, также называемый « утечкой памяти») (edit: derp, конечно, это не не правда)
Вы хотите «вещать» что-то на коллекцию объектов, но не хотите, чтобы они поддерживали их жизнь; List<WeakReference>
может поддерживаться легко, и даже обрезают, удаляя , гдеref.Target == null