Permettez-moi de commencer par ceci et d'y revenir:
Un WeakReference est utile lorsque vous souhaitez garder un onglet sur un objet, mais vous NE voulez PAS que vos observations empêchent la collecte de cet objet.
Commençons donc depuis le début:
... convient à l'avance pour toute infraction non intentionnelle, mais je vais revenir au niveau "Dick et Jane" pour un moment, car on ne peut jamais dire à son public.
Ainsi, lorsque vous avez un objet X
- spécifions-le comme une instance de class Foo
- il NE PEUT PAS vivre seul (généralement vrai); De la même manière que "Aucun homme n'est une île", un objet ne peut être promu en îlot que de quelques manières - bien que cela s'appelle être une racine GC dans le langage CLR. Le fait d'être une racine du GC ou d'avoir une chaîne établie de connexions / références à une racine du GC est essentiellement ce qui détermine si les Foo x = new Foo()
déchets sont collectés ou non .
Si vous ne pouvez pas retrouver votre chemin vers une racine GC, soit en marchant en tas, soit en pile, vous êtes effectivement orphelin et serez probablement marqué / collecté au prochain cycle.
À ce stade, examinons quelques exemples horribles:
Tout d'abord, notre 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--);
}
}
Assez simple - il n'est pas thread-safe, donc n'essayez pas cela, mais conserve un "décompte de références" approximatif d'instances actives et diminue lorsqu'elles sont finalisées.
Maintenant, regardons un 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");
}
}
Nous avons donc un objet qui est déjà une racine GC propre (enfin ... pour être précis, il sera enraciné via une chaîne directement dans le domaine d'applications exécutant cette application, mais c'est un autre sujet) qui a deux méthodes. de verrouiller sur une Foo
instance - testons-le:
// 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();
Maintenant, à partir de ce qui précède, vous attendez-vous à ce que l'objet-qui-était-une-fois-mentionné-par- f
soit soit "collectionable"?
Non, car il existe maintenant un autre objet contenant une référence, le Dictionary
dans cette Singleton
instance statique.
Ok, essayons l'approche faible:
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();
Maintenant, lorsque nous détruisons notre référence à Foo
-ce qui était autrefois f
, il n'y a plus de références "dures" à l'objet, il est donc collectable - la WeakReference
création par le faible auditeur ne l'empêchera pas.
Bons cas d'utilisation:
Gestionnaires d'événements (bien que lisez ceci en premier: événements faibles en C # )
Vous avez une situation dans laquelle vous provoqueriez une "référence récursive" (l'objet A fait référence à l'objet B, qui fait référence à l'objet A, également appelé "fuite de mémoire") (edit: derp, bien sûr, il s'agit pas vrai)
Vous voulez "diffuser" quelque chose à une collection d'objets, mais vous ne voulez pas être la chose qui les garde en vie; un List<WeakReference>
peut être maintenu facilement, et même élagué en enlevant oùref.Target == null