Frage

Ich verstehe was System.WeakReference tut, aber was ich anscheinend nicht begreifen kann, ist ein praktisches Beispiel dafür, wofür es nützlich sein könnte.Die Klasse selbst scheint mir, nun ja, ein Hack zu sein.Mir scheint, dass es andere, bessere Möglichkeiten gibt, ein Problem zu lösen, bei dem in den Beispielen, die ich gesehen habe, eine WeakReference verwendet wird.Was ist das kanonische Beispiel dafür, wo Sie wirklich eine WeakReference verwenden müssen?Versuchen wir nicht, es zu bekommen? weiter weg von dieser Art von Verhalten und Nutzung dieser Klasse?

War es hilfreich?

Lösung

Ein nützliches Beispiel sind die Leute, die die objektorientierte DB4O-Datenbank betreiben.Dort werden WeakReferences als eine Art Light-Cache verwendet:Dadurch bleiben Ihre Objekte nur so lange im Speicher wie Ihre Anwendung, sodass Sie einen echten Cache darüber legen können.

Eine andere Verwendung wäre die Implementierung schwacher Event-Handler.Eine der Hauptursachen für Speicherlecks in .NET-Anwendungen ist derzeit das Vergessen, Ereignishandler zu entfernen.Z.B.

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

Sehen Sie das Problem?Im obigen Snippet bleibt MyForm für immer im Speicher erhalten, solange MyApplication im Speicher aktiv ist.Erstellen Sie 10 MyForms, schließen Sie sie alle. Ihre 10 MyForms bleiben weiterhin im Speicher und werden vom Ereignishandler am Leben gehalten.

Geben Sie WeakReference ein.Sie können mit WeakReferences einen schwachen Event-Handler erstellen, sodass someHandler ein schwacher Event-Handler für MyApplication.Foo ist, und so Ihre Speicherlecks beheben!

Das ist nicht nur Theorie.Dustin Campbell vom DidItWith.NET-Blog gepostet eine Implementierung schwacher Event-Handler mit System.WeakReference.

Andere Tipps

Ich verwende es, um einen Cache zu implementieren, in dem nicht verwendete Einträge automatisch durch Müll gesammelt werden:

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;
     }

    /* ... */
   }

Ich verwende schwache Referenzen für die Zustandshaltung in Mixins.Denken Sie daran, dass Mixins statisch sind. Wenn Sie also ein statisches Objekt verwenden, um den Status an ein nicht statisches Objekt anzuhängen, wissen Sie nie, wie lange es dauern wird.Anstatt also eine zu behalten Dictionary<myobject, myvalue> Ich behalte eine Dictionary<WeakReference,myvalue> um zu verhindern, dass das Mixin die Dinge zu lange in die Länge zieht.

Das einzige Problem ist, dass ich bei jedem Zugriff auch nach toten Referenzen suche und diese entferne.Nicht, dass sie irgendjemandem schaden würden, es sei denn, es wären Tausende.

Es gibt zwei Gründe, warum Sie es verwenden würden WeakReference.

  1. Anstelle globaler Objekte, die als statisch deklariert sind:Globale Objekte werden als statische Felder deklariert und statische Felder können bis zum GC nicht durch Müll gesammelt werden AppDomain ist GC'ed.Sie riskieren also Ausnahmen wegen unzureichendem Arbeitsspeicher.Stattdessen können wir das globale Objekt in a einschließen WeakReference.Obwohl die WeakReference selbst als statisch deklariert ist, wird das Objekt, auf das es zeigt, einer GC unterzogen, wenn der Speicher knapp wird.

    Grundsätzlich verwenden wrStaticObject anstatt staticObject.

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

    Einfache App, um zu beweisen, dass ein statisches Objekt zusammen mit der AppDomain im Garbage Collection-Speicher erfasst wird.

    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); }
    }
    

    Hinweis aus der Ausgabe unten staticThing wird auch danach ganz am Ende GC'ed ThingsWrapper ist - d.h.GC'ed wann AppDomain ist GC'ed.

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

    Stattdessen können wir einpacken Thing in einem WeakReference.Als wrStaticThing GC-fähig sein kann, benötigen wir eine Lazy-Loaded-Methode, die ich der Kürze halber weggelassen habe.

    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
    }
    

    Beachten Sie die Ausgabe darunter wrStaticThing wird GC-gesteuert, wenn der GC-Thread aufgerufen wird.

    Thing() wrStaticThing
    ~Thing() wrStaticThing
    ~WeakReferenceThing
    WeakReference is dead.
    
  2. Für Objekte, deren Initialisierung zeitaufwändig ist:Sie möchten nicht, dass Objekte, deren Initialisierung zeitaufwändig ist, einer GC unterzogen werden.Sie können entweder eine statische Referenz beibehalten, um dies zu vermeiden (mit den oben genannten Nachteilen), oder sie verwenden WeakReference.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top