2010-05-06 10 views
25

Wo kann ich eine gute Implementierung von IDictionary finden, die schwache Referenzen im Inneren verwendet?Gute Implementierung von schwachen Wörterbuch in. NET

Wörterbuch sollte nur schwache Verweise auf Werte abhalten und schließlich aufzuräumen sich von toten Referenzen.

Oder sollte ich es einfach selbst schreiben?

+0

http://blogs.msdn.com/b/nicholg/archive/2006/06/04/617466.aspx – Mark

+3

Obwohl es kein 'IDictionary 'ist, ist die [ConditionalWeakTable] (http://msdn.microsoft.wiktionary.org com/de-us/library/dd287757.aspx) habe ich wirklich gesucht, als Google mich hierher brachte. Danke an [diese Antwort] (http://stackoverflow.com/questions/5764556/best-time-to-cull-weakreferences-in-a-collection-in-net/5764855#5764855). –

+0

Tolle Entdeckung! –

Antwort

24

ConditionalWeakTable Class verwendet schwache Schlüssel und entfernt den Schlüssel/Wert-Eintrag automatisch, sobald keine weiteren Verweise auf einen Schlüssel außerhalb der Tabelle vorhanden sind.

+2

Es sollte beachtet werden, dass diese Klasse 'ReferenceEquals' anstelle von' GetHashCode' und 'Equals' verwendet, um Gleichheitsprüfungen durchzuführen. Eine ausführlichere Diskussion finden Sie unter http://stackoverflow.com/a/8441180/167251. – larsmoa

+16

Das OP fragt nach einem schwachen Wörterbuch mit schwachen ** Werten **. Die .NET-Klasse "ConditionalWeakTable" ist ein schwaches Wörterbuch mit schwachen ** Schlüsseln **. Daher glaube ich nicht, dass dies eine richtige Antwort ist. –

+2

Aber genau das suche ich: P also vielen Dank für die Bestätigung! – schwarz

6

Sie müssen es selbst schreiben. Es sollte relativ einfach sein, die IDictionary-Schnittstelle zu implementieren und dann die tatsächlichen Werte als WeakReferences zu speichern. Sie können dann die Werte von add/select überprüfen, um zu sehen, ob sie noch am Leben sind.

Pseudo-Code - nicht wirklich kompilieren:

public class WeakDictionary <TKey,TValue> : IDictionary<TKey,TValue> 
{ 
    private IDictionary<TKey,WeakReference> _innerDictionary = new Dictionary<TKey,WeakReference>(); 


    public TValue Index[ TKey key ] 
    { 
     get{ 
      var reference = _innerDictionary[ key ]; 
      if(reference.IsAlive) 
       return (TValue)reference.Target; 
      throw new InvalidOperation("Key not found."); 
     } 

    } 

    private void Cull() 
    { 
     var deadKeys = new List<TKey>(); 
     foreach(var pair in _innerDictionary) 
     { 
      if(! pair.Value.IsAlive) 
       deadKeys.Add(pair.Key); 
     } 

     foreach(var key in deadKeys) 
      _innerDictionary.Remove(key); 
    } 
} 
+8

Seien Sie vorsichtig, dass Garbage Collection zwischen 'reference.IsAlive' und 'reference.Target', wodurch diese Lösung für Rennbedingungen anfällig wird. –

+0

Es ist einfach genug, Target auf deinen eigenen Stack zu legen, bevor du IsAlive kontrollierst, das das Rennen repariert –

+1

Warum benutzt du WeakReference.TryGetValue nicht? – TamusJRoyce

0

Es ist eine Sache WeakReferences auf Werte haben, aber ich fand, dass Wörterbuch Tasten auch eine Quelle von Speicherlecks sein können. Hier ist eine nackte Knochen-Implementierung mit WeakReference Schlüsseln:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace Common.library.collections { 

    /// <summary> 
    /// THIS DICTIONARY WILL NOT "HANG ON" TO THE KEYS IT USES 
    /// IF THE KEY IS GARBAGE COLLECTED, THE VALUE WILL BE RELEASED TOO 
    /// </summary> 
    public class Dictionary_usingWeakKey<K, V> { 
     //MAP FROM HASH CODE TO LIST OF KEY/VALUE PAIRS 
     private Dictionary<int, List<Pair>> dic = new Dictionary<int, List<Pair>>(); 


     public void Add(K key, V value) { 
      if (value==null){ 
       this.Remove(key); 
       return; 
      }//endif 

      List<Pair> list = null; 
      dic.TryGetValue(key.GetHashCode(), out list); 
      if (list == null) { 
       list = new List<Pair>(); 
       dic.Add(key.GetHashCode(), list); 
      }//endif 

      Boolean isDirty = false;    
      foreach(Pair p in list){ 
       if (p.Key.Target == null) { 
        isDirty = true; 
        continue; 
       }//endif 
       if (p.Key.Target == (Object)key) { 
        p.Value = (Object)value; 
        if (isDirty) cleanList(list); 
        return; 
       }//endif 
      }//for 
      if (isDirty) cleanList(list); 

      Pair newP=new Pair(); 
      newP.Key = new WeakReference(key); 
      newP.Value = value; 
      list.Add(newP); 
     }//method 


     public bool ContainsKey(K key) { 
      List<Pair> list = null; 
      dic.TryGetValue(key.GetHashCode(), out list); 
      if (list == null) return false; 

      Boolean isDirty = false; 
      foreach (Pair p in list) { 
       if (p.Key.Target == null) { 
        isDirty = true; 
        continue; 
       }//endif 
       if (p.Key.Target == (Object)key) { 
        if (isDirty) cleanList(list); 
        return true; 
       }//endif 
      }//for 
      if (isDirty) cleanList(list); 

      return false; 
     }//method 



     private void cleanList(List<Pair> list) { 
      var temp = (from Pair p in list where p.Key.Target != null select p); 
      list.Clear(); 
      list.AddRange(temp); 
     }//method 



     public bool Remove(K key) { 
      List<Pair> list = null; 
      dic.TryGetValue(key.GetHashCode(), out list); 
      if (list == null) return true; 

      foreach (Pair p in list) { 
       if (p.Key.Target == (Object)key) { 
        p.Value = null; 
        break; 
       }//endif 
      }//for 
      cleanList(list); 

      return true; 
     }//method 





     public V this[K key] { 
      get { 
       List<Pair> list = null; 
       dic.TryGetValue(key.GetHashCode(), out list); 
       if (list == null) return default(V); 

       Boolean isDirty = false; 
       foreach (Pair p in list) { 
        if (p.Key.Target == null) { 
         isDirty = true; 
         continue; 
        }//endif 

        if (p.Key.Target == (Object)key) { 
         if (isDirty) cleanList(list); 
         return (V)p.Value; 
        }//endif 
       }//for 
       if (isDirty) cleanList(list); 

       return default(V); 
      } 
      set { 
       this.Add(key, value); 
      } 
     } 


     public void Add(KeyValuePair<K, V> item) { 
      throw new NotImplementedException(); 
     } 

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

     public bool Contains(KeyValuePair<K, V> item) { 
      throw new NotImplementedException(); 
     } 

     public void CopyTo(KeyValuePair<K, V>[] array, int arrayIndex) { 
      throw new NotImplementedException(); 
     } 

     public int Count { 
      get { 
       throw new NotImplementedException();    
       //return dic.Count();   
      } 
     } 

     public bool IsReadOnly { 
      get { return false; } 
     } 

     public bool Remove(KeyValuePair<K, V> item) { 
      throw new NotImplementedException(); 
     } 



     public IEnumerator<KeyValuePair<K, V>> GetEnumerator() { 
      throw new NotImplementedException();  
      //return dic.GetEnumerator(); 
     } 


     //System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { 
     // return ((System.Collections.IEnumerable)dic).GetEnumerator(); 
     //} 





    }//class 



    public class Pair{ 
     public WeakReference Key; 
     public Object Value; 
    }//method 

} 
+0

Ist es von Java portiert? –

+0

Dies wird von keinem Java-Code portiert –

0

Ein Problem mit einfach ein Wörterbuch der WeakReference Objekte hält, ist, dass es keine Möglichkeit gibt, kurz das gesamte Wörterbuch aufzählt, von aus dem Wörterbuch zu entfernen alle WeakReference Objekte, deren Ziele gehen aus dem Rahmen.

Es wäre hilfreich, wenn ein WeakReference einen Delegierten enthalten könnte, die aufgerufen werden würde, wenn das primäre Ziel außerhalb des Bereichs ging. Soweit ich weiß, gibt es keine Möglichkeit, das zu tun. Wenn es Ihnen nichts ausmacht, den Objekten, die Sie in Ihrem "schwachen Wörterbuch" speichern, ein weiteres Feld und ein wenig Code hinzuzufügen, würde ich vorschlagen, ein "Finasposer" -Objekt zu erstellen, dessen einziges Feld ein MethodInvoker ist; Wenn sie entsorgt werden, sollte der MethodInvoker beendet werden; Der Finalizer sollte Interlocked.Exchange() den MethodInvoker auf null setzen und - wenn sein alter Wert nicht null ist - ihn aufrufen. Das Objekt, das in das Wörterbuch geschrieben werden soll, sollte ein neues Finasposer-Objekt mit einem Delegaten erstellen, der dazu führt, dass der Schlüssel aus dem Wörterbuch entfernt wird, wenn dies praktisch ist.

Beachten Sie, dass das weder der Finalizerthread noch ein aufgerufen Delegierten dadurch sollte nie direkt das Wörterbuch manipulieren, noch irgendetwas tun, der eine Sperre zu erwerben erfordern würde. Wenn der Finasposer einen Delegaten hält, ist dieser Delegat selbst gültig, wenn Finalize ausgeführt wird, aber das Objekt, das dem Delegaten zugeordnet ist, und alle Objekte, auf die es verweist, befinden sich möglicherweise in unerwarteten Status. Es sollte jedoch für die von Finasposer aufgerufene Methode sicher sein, zu einer verknüpften Liste einen Verweis auf das Objekt hinzuzufügen, das den Gültigkeitsbereich verlassen hat. Die Add-, Remove- und andere Methoden des Lexikons könnten die verknüpfte Liste abfragen, um zu sehen, ob irgendwelche der WeakReferences darin gestorben waren und entfernt werden mußten.

+0

Es gibt eine Möglichkeit, eine Benachrichtigung zu erhalten, nachdem das Objekt gesammelt wurde. ConditionalWeakTable kann hier helfen. Bitte beachten Sie mein Blog [WeakTable] (http://www.nesterovsky-bros.com/weblog/2014/01/08/WeakTable.aspx) für Details. –

0

Wenn Identitätsvergleich kann dann nicht verwendet werden ConditionalWeakTable ist keine Option.

In diesem Fall habe ich es wagen, unsere Implementierung vorschlagen WeakTable.cs, und unsere Beschreibung im Blog WeakTable.