2008-08-19 12 views
33

Ich verstehe, was System.WeakReference tut, aber was ich nicht zu begreifen scheinen, ist ein praktisches Beispiel für was es nützlich sein könnte. Die Klasse selbst scheint mir ein Hack zu sein. Es scheint mir, dass es andere, bessere Mittel zur Lösung eines Problems gibt, bei denen eine schwacheReferenz in Beispielen verwendet wird, die ich gesehen habe. Was ist das kanonische Beispiel dafür, wo du wirklich eine WeakReference verwenden musst? Versuchen wir nicht, weiter weg von dieser Art von Verhalten und Verwendung dieser Klasse zu bekommen?Praktische Anwendung von System.WeakReference

Antwort

44

Ein nützliches Beispiel hierfür sind die Jungs, die DB4O objektorientierte Datenbank. Dort werden WeakReferences als eine Art Licht-Cache verwendet: Sie behalten Ihre Objekte nur so lange im Speicher, wie Ihre Anwendung es tut, sodass Sie einen echten Cache darauf legen können.

Eine andere Verwendung wäre in der Implementierung von schwachen Event-Handler. Derzeit ist es eine große Quelle von Speicherverlusten in .NET-Anwendungen, Event-Handler zu entfernen. Z.B.

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

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

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

Dies ist nicht nur Theorie. Dustin Campbell aus dem Blog DidItWith.NET veröffentlichte an implementation of weak event handlers mit System.WeakReference.

+3

+1 für den Tipp mit dem Eventhandler, das ist genial !!!! –

+0

Ja, es ist ziemlich raffiniert. Wir haben seinen Code hier bei der Arbeit übernommen, mit einigen Zusätzen, um andere Arten von Event-Handlern zu behandeln (z.B. den nicht-generischen EventHandler, PropertyChangedEventHandler, etc.). Es hat sehr gut für uns geklappt. –

12

ich es verwenden, um einen Cache zu implementieren, wo nicht verwendete Einträge automatisch Müll gesammelt sind:

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

    /* ... */ 
    } 
2

Ich verwende schwache Referenz für die State-keeping in Mixins. Denken Sie daran, Mixins sind statisch. Wenn Sie also ein statisches Objekt verwenden, um den Status an ein nicht-statisches Objekt anzuhängen, wissen Sie nie, wie lange es benötigt wird. Anstatt also eine Dictionary<myobject, myvalue> zu behalten, behalte ich eine Dictionary<WeakReference,myvalue>, um zu verhindern, dass das Mixin Dinge zu lange schleppt.

Das einzige Problem ist, dass jedes Mal, wenn ich einen Zugriff mache, ich auch nach toten Referenzen suche und sie entferne. Nicht, dass sie irgendjemanden verletzen, außer es gibt natürlich Tausende.

0

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

  1. Statt globaler Objekte deklariert als statische: Globale Objekte werden als statische Felder und statische Felder deklariert sind, können nicht bis zur AppDomain GC'ed (Garbage Collection) ist GC'ed. So riskieren Sie Out-of-Memory-Ausnahmen. Stattdessen können wir das globale Objekt in einem WeakReference umbrechen. Obwohl das WeakReference selbst als statisch deklariert ist, wird das Objekt, auf das es zeigt, GC'ed, wenn der Speicher niedrig ist.

    Verwenden Sie grundsätzlich wrStaticObject statt staticObject.

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

    Einfache App zu beweisen, dass statische Objekt ist Müll gesammelt, wenn AppDomain ist.

    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 unter staticThing wird ganz am Ende GC'ed auch nach ThingsWrapper ist - das heißt, wenn GC'ed AppDomain ist GC'ed.

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

    Stattdessen können wir Thing in einem WeakReference wickeln. Als wrStaticThing kann GC'ed werden, 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 
    } 
    

    Hinweis vom Ausgang unterhalb dieser wrStaticThing GC'ed wird, wenn GC Thread aufgerufen wird.

    Thing() wrStaticThing 
    ~Thing() wrStaticThing 
    ~WeakReferenceThing 
    WeakReference is dead. 
    
  2. Für Objekte, die initialisieren zeitaufwendig sind: Sie wollen nicht Objekte, die zeit consusming sind init GC'ed werden. Sie können entweder eine statische Referenz beibehalten, um dies zu vermeiden (mit Nachteile von oben) oder WeakReference verwenden.