.net dictionary weak-references

Buena implementación de diccionario débil en.Net



dictionary weak-references (5)

Si no se puede usar la comparación de identidad, ConditionalWeakTable no es una opción.

En este caso, me atrevo a sugerir nuestra implementación WeakTable.cs , y nuestra descripción en el blog WeakTable .

¿Dónde puedo encontrar una buena implementación de IDictionary que use referencias débiles dentro?

El diccionario debe contener solo referencias débiles a los valores y, finalmente, limpiarse de referencias muertas.

¿O debería simplemente escribirlo yo mismo?


Tendrás que escribirlo tú mismo. Debe ser relativamente sencillo, implementando la interfaz IDictionary y luego almacenando los valores reales como WeakReferences. Luego puede verificar los valores en Agregar / seleccionar para ver si todavía están vivos.

Pseudo código - realmente no compilará:

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


Un problema con simplemente sostener un diccionario de objetos WeakReference es que no hay forma, aparte de enumerar todo el diccionario, de eliminar del Diccionario cualquier objeto WeakReference cuyos objetivos queden fuera del alcance.

Sería útil que una WeakReference pudiera incluir un delegado que se invocaría cuando el objetivo principal quedara fuera del alcance. Que yo sepa, no hay manera de hacer eso. Si no le importa agregar otro campo y un pequeño código a los objetos que está almacenando dentro de su "diccionario débil", sugeriría crear lo que yo llamo un objeto "Finasposer", cuyo único campo es MethodInvoker; una vez eliminado, el MethodInvoker debe ser anulado; el finalizador debe Interlocked.Exchange () el MethodInvoker a null y, si su valor anterior no era nulo, lo invoca. El objeto que se debe escribir en el diccionario debe crear un nuevo objeto Finasposer, con un delegado que hará que la clave se elimine del diccionario cuando sea conveniente.

Tenga en cuenta que ni el finalizador ni ningún delegado invocado por lo tanto nunca deben manipular directamente el diccionario, ni hacer nada que requiera la adquisición de un bloqueo. Si el Finasposer tiene un delegado, se garantiza que ese delegado en sí mismo será válido cuando se ejecute Finalize, pero el objeto adjunto al delegado, y cualquier objeto al que se haga referencia, puede estar en estados inesperados. Sin embargo, debería ser seguro que el método llamado Finasposer agregue a una lista vinculada una referencia al objeto que quedó fuera del alcance. Los métodos Add, Remove y otros del Diccionario pueden sondear la lista enlazada para ver si alguna de las Referencias Weak en el mismo había muerto y era necesario limpiarla.


Una cosa es tener referencias débiles a los valores, pero descubrí que las claves del diccionario también pueden ser una fuente de pérdidas de memoria. Aquí hay una implementación simple con WeakReference a las claves:

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 }


ConditionalWeakTable utiliza claves débiles y elimina automáticamente la entrada clave / valor tan pronto como no existan otras referencias a una clave fuera de la tabla.