Где можно найти хорошую реализацию IDictionary
, которая использует слабые ссылки внутри?
Словарь должен содержать только слабые ссылки на значения и в конечном итоге очищать себя от мертвых ссылок.
Или я должен просто написать это сам?
Где можно найти хорошую реализацию IDictionary
, которая использует слабые ссылки внутри?
Словарь должен содержать только слабые ссылки на значения и в конечном итоге очищать себя от мертвых ссылок.
Или я должен просто написать это сам?
ConditionalWeakTable Class использует слабые ключи и автоматически удаляет запись ключа/значения, как только никакие другие ссылки на ключ не существуют за пределами таблицы.
Вам нужно будет написать это самостоятельно. Он должен быть относительно прямым, реализуя интерфейс IDictionary, а затем сохраняя фактические значения как WeakReferences. Затем вы можете проверить значения в add/select, чтобы увидеть, все ли они живы.
Псевдокод - на самом деле не компилируется:
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 );
}
}
Одно дело иметь значения WeakReferences для значений, но я обнаружил, что ключи словаря также могут быть источником утечек памяти. Ниже приведена реализация bare-костей с помощью WeakReference для ключей:
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
}
Одна проблема с простое использование словаря объектов WeakReference заключается в том, что нет возможности перечислить весь словарь, чтобы удалить из словаря любые объекты WeakReference, цели которых выходят за рамки.
Было бы полезно, если бы WeakReference мог включать в себя делегат, который будет вызываться, когда основная цель выходит за рамки. Насколько я знаю, нет никакого способа сделать это. Если вы не возражаете добавить другое поле и небольшой код для объектов, которые вы храните в своем "слабом словаре", я бы предложил создать то, что я называю объектом "Finasposer", единственным полем которого является MethodInvoker; при размещении метод MethodInvoker должен быть исключен; финализатор должен Interlocked.Exchange() MethodInvoker для null и - если его старое значение было не нулевым - вызовите его. Объект, который должен быть записан в словаре, должен создать новый объект Finasposer с делегатом, который приведет к тому, что ключ будет удален из словаря, когда это удобно.
Обратите внимание, что ни финализатор, ни какой-либо делегированный им вызов никогда не должны напрямую манипулировать словарем и не делать ничего, что потребовало бы блокировки. Если Finasposer содержит делегат, этот делегат гарантированно будет действительным, когда Finalize будет выполняться, но объект, прикрепленный к делегату, и любые объекты, на которые ссылается, могут быть в неожиданных состояниях. Однако должно быть безопасно, чтобы метод, называемый Finasposer, добавлял в связанный список ссылку на объект, который вышел из области видимости. Словарь "Добавить", "Удалить" и другие методы могут опросить связанный список, чтобы увидеть, умерла ли какая-либо из WeakReferences в ней, и ее необходимо было очистить.
Если сравнение идентичности не может быть использовано, ConditionalWeakTable не является вариантом.
В этом случае я осмелюсь предложить нашу реализацию WeakTable.cs, и наше описание в блоге WeakTable.