Подтвердить что ты не робот

Хорошая реализация слабого словаря в .Net

Где можно найти хорошую реализацию IDictionary, которая использует слабые ссылки внутри?

Словарь должен содержать только слабые ссылки на значения и в конечном итоге очищать себя от мертвых ссылок.

Или я должен просто написать это сам?

4b9b3361

Ответ 1

ConditionalWeakTable Class использует слабые ключи и автоматически удаляет запись ключа/значения, как только никакие другие ссылки на ключ не существуют за пределами таблицы.

Ответ 2

Вам нужно будет написать это самостоятельно. Он должен быть относительно прямым, реализуя интерфейс 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 );
    }
}

Ответ 3

Одно дело иметь значения 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

}

Ответ 4

Одна проблема с простое использование словаря объектов WeakReference заключается в том, что нет возможности перечислить весь словарь, чтобы удалить из словаря любые объекты WeakReference, цели которых выходят за рамки.

Было бы полезно, если бы WeakReference мог включать в себя делегат, который будет вызываться, когда основная цель выходит за рамки. Насколько я знаю, нет никакого способа сделать это. Если вы не возражаете добавить другое поле и небольшой код для объектов, которые вы храните в своем "слабом словаре", я бы предложил создать то, что я называю объектом "Finasposer", единственным полем которого является MethodInvoker; при размещении метод MethodInvoker должен быть исключен; финализатор должен Interlocked.Exchange() MethodInvoker для null и - если его старое значение было не нулевым - вызовите его. Объект, который должен быть записан в словаре, должен создать новый объект Finasposer с делегатом, который приведет к тому, что ключ будет удален из словаря, когда это удобно.

Обратите внимание, что ни финализатор, ни какой-либо делегированный им вызов никогда не должны напрямую манипулировать словарем и не делать ничего, что потребовало бы блокировки. Если Finasposer содержит делегат, этот делегат гарантированно будет действительным, когда Finalize будет выполняться, но объект, прикрепленный к делегату, и любые объекты, на которые ссылается, могут быть в неожиданных состояниях. Однако должно быть безопасно, чтобы метод, называемый Finasposer, добавлял в связанный список ссылку на объект, который вышел из области видимости. Словарь "Добавить", "Удалить" и другие методы могут опросить связанный список, чтобы увидеть, умерла ли какая-либо из WeakReferences в ней, и ее необходимо было очистить.

Ответ 5

Если сравнение идентичности не может быть использовано, ConditionalWeakTable не является вариантом.

В этом случае я осмелюсь предложить нашу реализацию WeakTable.cs, и наше описание в блоге WeakTable.