Question

Je comprends quoi Système.WeakReference le fait, mais ce que je n'arrive pas à comprendre, c'est un exemple pratique de ce à quoi cela pourrait être utile.La classe elle-même me semble être un hack.Il me semble qu'il existe d'autres moyens, meilleurs, de résoudre un problème dans lequel une WeakReference est utilisée dans les exemples que j'ai vus.Quel est l'exemple canonique de l'endroit où vous devez vraiment utiliser une WeakReference ?N'essayons-nous pas d'obtenir plus loin loin de ce type de comportement et d'utilisation de cette classe ?

Était-ce utile?

La solution

Un exemple utile est celui des personnes qui exécutent la base de données orientée objet DB4O.Là, les WeakReferences sont utilisées comme une sorte de cache léger :il gardera vos objets en mémoire aussi longtemps que votre application, vous permettant de mettre un véritable cache par-dessus.

Une autre utilisation serait la mise en œuvre de gestionnaires d'événements faibles.Actuellement, l’une des principales sources de fuites de mémoire dans les applications .NET est l’oubli de supprimer les gestionnaires d’événements.Par exemple.

public MyForm()
{
    MyApplication.Foo += someHandler;
}

Vous voyez le problème ?Dans l'extrait ci-dessus, MyForm sera conservé en mémoire pour toujours tant que MyApplication sera vivant en mémoire.Créez 10 MyForms, fermez-les tous, vos 10 MyForms seront toujours en mémoire, maintenus en vie par le gestionnaire d'événements.

Entrez WeakReference.Vous pouvez créer un gestionnaire d'événements faibles en utilisant WeakReferences afin que someHandler soit un gestionnaire d'événements faibles pour MyApplication.Foo, corrigeant ainsi vos fuites de mémoire !

Ce n'est pas seulement de la théorie.Dustin Campbell du blog DidItWith.NET a publié une implémentation de gestionnaires d'événements faibles en utilisant System.WeakReference.

Autres conseils

Je l'utilise pour implémenter un cache où les entrées inutilisées sont automatiquement récupérées :

class Cache<TKey,TValue> : IEnumerable<KeyValuePair<TKey,TValue>>
{ Dictionary<TKey,WeakReference> dict = new Dictionary<TKey,WeakReference>();

   public TValue this[TKey key]
    { get {lock(dict){ return getInternal(key);}}
      set {lock(dict){ setInteral(key,value);}}     
    }

   void setInteral(TKey key, TValue val)
    { if (dict.ContainsKey(key)) dict[key].Target = val;
      else dict.Add(key,new WeakReference(val));
    } 


   public void Clear() { dict.Clear(); }

   /// <summary>Removes any dead weak references</summary>
   /// <returns>The number of cleaned-up weak references</returns>
   public int CleanUp()
    { List<TKey> toRemove = new List<TKey>(dict.Count);
      foreach(KeyValuePair<TKey,WeakReference> kv in dict)
       { if (!kv.Value.IsAlive) toRemove.Add(kv.Key);
       }

      foreach (TKey k in toRemove) dict.Remove(k);
      return toRemove.Count;
    }

    public bool Contains(string key) 
     { lock (dict) { return containsInternal(key); }
     }

     bool containsInternal(TKey key)
      { return (dict.ContainsKey(key) && dict[key].IsAlive);
      }

     public bool Exists(Predicate<TValue> match) 
      { if (match==null) throw new ArgumentNullException("match");

        lock (dict)
         { foreach (WeakReference weakref in dict.Values) 
            { if (   weakref.IsAlive 
                  && match((TValue) weakref.Target)) return true;
         }  
      }

       return false;
     }

    /* ... */
   }

J'utilise une référence faible pour le maintien de l'état dans les mixins.N'oubliez pas que les mixins sont statiques, donc lorsque vous utilisez un objet statique pour attacher un état à un objet non statique, vous ne savez jamais combien de temps cela sera nécessaire.Alors au lieu de garder un Dictionary<myobject, myvalue> je garde un Dictionary<WeakReference,myvalue> pour éviter que le mixin ne traîne les choses trop longtemps.

Le seul problème est que chaque fois que j'effectue un accès, je vérifie également les références mortes et les supprime.Non pas qu’ils aient blessé qui que ce soit, à moins qu’il y en ait des milliers, bien sûr.

Il y a deux raisons pour lesquelles vous utiliseriez WeakReference.

  1. Au lieu d'objets globaux déclarés comme statiques:Les objets globaux sont déclarés comme champs statiques et les champs statiques ne peuvent pas être GC (garbage-collected) jusqu'à ce que le AppDomain est GC.Vous risquez donc des exceptions de mémoire insuffisante.Au lieu de cela, nous pouvons envelopper l'objet global dans un WeakReference.Même si le WeakReference lui-même est déclaré statique, l'objet vers lequel il pointe sera GC lorsque la mémoire est faible.

    En gros, utilisez wrStaticObject au lieu de staticObject.

    class ThingsWrapper {
        //private static object staticObject = new object();
        private static WeakReference wrStaticObject 
            = new WeakReference(new object());
    }
    

    Application simple pour prouver que l'objet statique est récupéré lorsque AppDomain l'est.

    class StaticGarbageTest
    {
        public static void Main1()
        {
            var s = new ThingsWrapper();
            s = null;
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
    }
    class ThingsWrapper
    {
        private static Thing staticThing = new Thing("staticThing");
        private Thing privateThing = new Thing("privateThing");
        ~ThingsWrapper()
        { Console.WriteLine("~ThingsWrapper"); }
    }
    class Thing
    {
        protected string name;
        public Thing(string name) {
            this.name = name;
            Console.WriteLine("Thing() " + name);
        }
        public override string ToString() { return name; }
        ~Thing() { Console.WriteLine("~Thing() " + name); }
    }
    

    Remarque tirée de la sortie ci-dessous staticThing est GC à la toute fin même après ThingsWrapper est - c'est-à-direGC'ed quand AppDomain est GC.

    Thing() staticThing
    Thing() privateThing
    ~Thing() privateThing
    ~ThingsWrapper
    ~Thing() staticThing
    

    Au lieu de cela, nous pouvons envelopper Thing dans un WeakReference.Comme wrStaticThing peut être GC, nous aurons besoin d'une méthode à chargement paresseux que j'ai laissée de côté par souci de brièveté.

    class WeakReferenceTest
    {
        public static void Main1()
        {
            var s = new WeakReferenceThing();
            s = null;
            GC.Collect();
            GC.WaitForPendingFinalizers();
            if (WeakReferenceThing.wrStaticThing.IsAlive)
                Console.WriteLine("WeakReference: {0}", 
                    (Thing)WeakReferenceThing.wrStaticThing.Target);
            else 
                Console.WriteLine("WeakReference is dead.");
        }
    }
    class WeakReferenceThing
    {
        public static WeakReference wrStaticThing;
        static WeakReferenceThing()
        { wrStaticThing = new WeakReference(new Thing("wrStaticThing")); }
        ~WeakReferenceThing()
        { Console.WriteLine("~WeakReferenceThing"); }
        //lazy-loaded method to new Thing
    }
    

    Notez à partir de la sortie ci-dessous que wrStaticThing est GC lorsque le thread GC est invoqué.

    Thing() wrStaticThing
    ~Thing() wrStaticThing
    ~WeakReferenceThing
    WeakReference is dead.
    
  2. Pour les objets dont l'initialisation prend du temps:Vous ne voulez pas que les objets dont l'initialisation prend beaucoup de temps soient GC.Vous pouvez soit conserver une référence statique pour éviter cela (avec les inconvénients du point ci-dessus), soit utiliser WeakReference.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top