33

IDictionary内部で弱参照を使用する適切な実装はどこにありますか?

ディクショナリは値への弱参照のみを保持し、最終的にはデッド参照をクリーンアップする必要があります。

それとも私はそれを自分で書くべきですか?

4

7 に答える 7

41

ConditionalWeakTable クラスは弱いキーを使用し、キーへの他の参照がテーブルの外に存在しなくなるとすぐに、キー/値エントリを自動的に削除します。

于 2012-10-17T07:13:47.343 に答える
5

自分で書く必要があります。IDictionary<T,T>インターフェイスを実装してから、実際の値をWeakReferences<T>. その後、 add/select を使用して値をチェックし、TryGetTargetそれらがまだ生きているかどうかを確認できます。

public class WeakDictionary <TKey,TValue> : IDictionary<TKey,TValue>
    where TValue : class
{
    private readonly Dictionary<TKey,WeakReference<TValue>> innerDictionary = new Dictionary<TKey,WeakReference<TValue>>();
    
    
    public TValue Index[ TKey key ]
    {
        get
        {
            // Use .TryGetTarget instead of .IsAlive and .Target
            if (this.innerDictionary.TryGetValue(key, out WeakReference<TValue> wf) && wf.TryGetTarget(out TValue value))
            {
                return value;
            }

            return null;
        }
        
    }
    
    private void Cull()
    {
        var deadKeys = this.innerDictionary.Where(kvp => kvp.Value.IsAlive).Select(kvp => kvp.Key).ToList();

        foreach (var key in deadKeys)
        {
            _ = this.innerDictionary.TryRemove(key);
        }
    }
}
于 2010-05-06T20:47:14.263 に答える
4

WeakReferenceオブジェクトのディクショナリを保持するだけの問題の1つは、ディクショナリ全体を列挙する以外に、ターゲットがスコープ外になるWeakReferenceオブジェクトをディクショナリから削除する方法がないことです。

プライマリターゲットがスコープ外になったときに呼び出されるデリゲートをWeakReferenceに含めることができれば便利です。私の知る限り、それを行う方法はありません。「弱い辞書」内に格納しているオブジェクトに別のフィールドと小さなコードを追加してもかまわない場合は、「Finasposer」オブジェクトと呼ばれるものを作成することをお勧めします。このオブジェクトの唯一のフィールドはMethodInvokerです。廃棄する場合、MethodInvokerは無効にする必要があります。ファイナライザーは、MethodInvokerをInterlocked.Exchange()してnullにし、(古い値がnullでない場合は)それを呼び出す必要があります。ディクショナリに書き込まれるオブジェクトは、新しいFinasposerオブジェクトを作成する必要があります。デリゲートを使用すると、都合のよいときにキーがディクショナリから削除されます。

ファイナライザーもそれによって呼び出されるデリゲートも、辞書を直接操作したり、ロックの取得を必要とするようなことをしたりしてはならないことに注意してください。Finasposerがデリゲートを保持している場合、Finalizeの実行時にそのデリゲート自体が有効であることが保証されますが、デリゲートにアタッチされているオブジェクト、およびそれによって参照されるオブジェクトは、予期しない状態になる可能性があります。ただし、Finasposerで呼び出されたメソッドが、スコープ外になったオブジェクトへの参照をリンクリストに追加することは安全です。辞書のAdd、Remove、およびその他のメソッドは、リンクリストをポーリングして、その中のWeakReferencesのいずれかが死んでいて、クリーンアップする必要があるかどうかを確認できます。

于 2010-11-26T19:48:15.957 に答える
1

値へのWeakReferencesがあることは1つのことですが、ディクショナリキーもメモリリークの原因になる可能性があることがわかりました。キーへの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

}
于 2010-11-01T14:49:55.040 に答える