Deixe-me colocar isso em primeiro lugar e voltar a ele:
Um WeakReference é útil quando você deseja manter abas em um objeto, mas NÃO deseja que suas observações impeçam a coleta desse objeto.
Então, vamos começar do começo:
- desculpe-me antecipadamente por qualquer ofensa não intencional, mas voltarei ao nível "Dick and Jane" por um momento, já que nunca se pode contar para o público.
Então, quando você tem um objeto X
- vamos especificá-lo como uma instância class Foo
dele - ele NÃO PODE viver sozinho (principalmente verdadeiro); Da mesma maneira que "Nenhum homem é uma ilha", existem apenas algumas maneiras pelas quais um objeto pode ser promovido para a Ilha da Ilha - embora seja chamado de raiz da GC no CLR. Ser uma raiz do GC ou ter uma cadeia estabelecida de conexões / referências a uma raiz do GC é basicamente o que determina se o Foo x = new Foo()
lixo é coletado ou não .
Se você não conseguir voltar para alguma raiz do GC por meio de heap ou stack stack, ficará efetivamente órfão e provavelmente será marcado / coletado no próximo ciclo.
Neste ponto, vejamos alguns exemplos horríveis:
Primeiro, nosso 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--);
}
}
Bastante simples - não é seguro para threads, por isso não tente fazer isso, mas mantém uma "contagem de referência" aproximada de instâncias ativas e diminui quando elas são finalizadas.
Agora vamos dar uma olhada em 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");
}
}
Portanto, temos um objeto que já é uma raiz própria do GC (bem ... para ser específico, ele será enraizado através de uma cadeia diretamente no domínio do aplicativo que está executando esse aplicativo, mas esse é outro tópico) que possui dois métodos de travar em uma Foo
instância - vamos testá-lo:
// 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();
Agora, pelo exposto, você esperaria que o objeto que antes era referido f
fosse "colecionável"?
Não, porque agora existe outro objeto com uma referência a ele - o Dictionary
nessa Singleton
instância estática.
Ok, vamos tentar a abordagem fraca:
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();
Agora, quando analisamos nossa referência ao Foo
que era uma vez f
, não há mais referências "difíceis" ao objeto, portanto ele é colecionável - o WeakReference
criado pelo ouvinte fraco não impede isso.
Bons casos de uso:
Manipuladores de eventos (embora leia isto primeiro: Eventos Fracos em C # )
Você tem uma situação em que poderia causar uma "referência recursiva" (ou seja, o objeto A se refere ao objeto B, que se refere ao objeto A, também conhecido como "vazamento de memória") (edit: derp, é claro que isso não é é verdade)
Você deseja "transmitir" algo para uma coleção de objetos, mas não quer ser o que os mantém vivos; um List<WeakReference>
pode ser mantido facilmente e até podado removendo onderef.Target == null