Déjame poner esto primero y volver a ello:
Una referencia débil es útil cuando desea mantener pestañas en un objeto, pero NO desea que sus observaciones eviten que se recopile ese objeto
Entonces, comencemos desde el principio:
- Disculpas de antemano por cualquier ofensa no intencional, pero voy a retroceder al nivel de "Dick y Jane" por un momento ya que uno nunca puede decirle a la audiencia.
Entonces, cuando tienes un objeto X
, especificémoslo como una instancia de class Foo
, NO PUEDE vivir solo (principalmente cierto); De la misma manera que "Ningún hombre es una isla", solo hay unas pocas formas en que un objeto puede promoverse a Islandhood, aunque se llama ser una raíz GC en CLR. Ser una Raíz GC, o tener una cadena establecida de conexiones / referencias a una raíz GC, es básicamente lo que determina si Foo x = new Foo()
se recolecta o no la basura.
Si no puede caminar de regreso a alguna raíz del GC, ya sea caminando por el montón o apilando, queda efectivamente huérfano y probablemente será marcado / recogido el próximo ciclo.
En este punto, veamos algunos ejemplos horriblemente inventados:
Primero, nuestro 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 simple: no es seguro para subprocesos, así que no intente eso, pero mantiene un "recuento de referencias" aproximado de instancias activas y decrementos cuando se finalizan.
Ahora echemos un vistazo a 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");
}
}
Entonces, tenemos un objeto que ya es una raíz de GC propia (bueno ... para ser específicos, se enraizará a través de una cadena directamente al dominio de la aplicación que ejecuta esta aplicación, pero ese es otro tema) que tiene dos métodos de engancharse a una Foo
instancia - probémoslo:
// 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();
Ahora, a partir de lo anterior, ¿esperaría que el objeto al que se hizo referencia alguna vez f
fuera "coleccionable"?
No, porque ahora hay otro objeto que tiene una referencia a él, el Dictionary
de esa Singleton
instancia estática.
Ok, intentemos con el enfoque débil:
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();
Ahora, cuando criticamos nuestra referencia a lo Foo
que fue una vez f
, no hay más referencias "duras" al objeto, por lo que es coleccionable: lo WeakReference
creado por el oyente débil no lo impedirá.
Buenos casos de uso:
Controladores de eventos (aunque lea esto primero: Eventos débiles en C # )
Tienes una situación en la que causarías una "referencia recursiva" (es decir, el objeto A se refiere al objeto B, que se refiere al objeto A, también conocido como "Fuga de memoria") (editar: derp, por supuesto, esto no es no es cierto)
Desea "transmitir" algo a una colección de objetos, pero no quiere ser lo que los mantenga vivos; a List<WeakReference>
puede mantenerse fácilmente e incluso podarse quitando donderef.Target == null