在.Net中实现弱字典

时间:2022-11-08 16:09:59

Where can I find good implementation of IDictionary which uses weak references inside?

我在哪里可以找到使用弱引用的IDictionary的良好实现?

Dictionary should be holding only weak references to values and eventually clean up itself of dead references.

字典应仅包含对值的弱引用,并最终清除死引用本身。

Or should I just write it myself?

或者我应该自己写一下?

5 个解决方案

#1


24  

ConditionalWeakTable Class uses weak keys and automatically removes the key/value entry as soon as no other references to a key exist outside the table.

ConditionalWeakTable类使用弱键,并在表外没有其他对键的引用时自动删除键/值条目。

#2


5  

You'll need to write it yourself. It should be relatively straight forward, implementing the IDictionary interface and then storing the actual values as WeakReferences. You can then check the values on add/select to see if they're still alive.

你需要自己写。它应该相对简单,实现IDictionary接口,然后将实际值存储为WeakReferences。然后,您可以检查添加/选择的值,以查看它们是否仍然存在。

Pseudo code - won't really compile:

伪代码 - 不会真正编译:

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


0  

It is one thing to have WeakReferences to values, but I found that Dictionary keys can also be a source of memory leaks. Here is a bare bones implementation with WeakReference to keys:

将WeakReferences设置为值是一回事,但我发现Dictionary键也可能是内存泄漏的来源。这是一个使用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


0  

One problem with simply holding a dictionary of WeakReference objects is that there's no way, short of enumerating the entire dictionary, of removing from the Dictionary any WeakReference objects whose targets go out of scope.

简单地保存WeakReference对象字典的一个问题是,除了枚举整个字典之外,没有办法从Dictionary中删除任何目标超出范围的WeakReference对象。

It would be helpful if a WeakReference could include a delegate which would be invoked when the primary target went out of scope. As far as I know, there's no way to do that. If you don't mind adding another field and a little code to the objects you're storing within your "weak dictionary", I'd suggest creating what I call a "Finasposer" object, whose sole field is a MethodInvoker; when disposed, the MethodInvoker should be nulled out; the finalizer should Interlocked.Exchange() the MethodInvoker to null and--if its old value was non-null--invoke it. The object to be written in the dictionary should create a new Finasposer object, with a delegate that will cause the key to be removed from the dictionary when convenient.

如果WeakReference可以包含在主要目标超出范围时将调用的委托,将会很有帮助。据我所知,没有办法做到这一点。如果你不介意在你的“弱词典”中存储的对象中添加另一个字段和一些代码,我建议创建我称之为“Finasposer”的对象,其唯一字段是MethodInvoker;处置时,MethodInvoker应该被废除;终结器应该Interlocked.Exchange()将MethodInvoker设置为null,如果其旧值为非null,则调用它。要在字典中写入的对象应该创建一个新的Finasposer对象,其中一个委托将在方便时将该键从字典中删除。

Note that the neither the finalizer nor any delegate invoked thereby should never directly manipulate the dictionary, nor do anything that would require acquiring a lock. If the Finasposer holds a delegate, that delegate itself is guaranteed to be valid when Finalize executes, but the object attached to the delegate, and any objects referenced thereby, may be in unexpected states. It should be safe, however, for the Finasposer-called method to add to a linked list a reference to the object that went out of scope. The Dictionary's Add, Remove, and other methods could poll the linked list to see if any of the WeakReferences therein had died and needed to be cleaned out.

请注意,终结器和由此调用的任何委托都不应该直接操作字典,也不应该做任何需要获取锁定的事情。如果Finasposer持有委托,则该委托本身在Finalize执行时保证有效,但附加到委托的对象以及由此引用的任何对象可能处于意外状态。但是,对于Finasposer调用的方法来说,添加到链表中的对超出范围的对象的引用应该是安全的。 Dictionary的Add,Remove和其他方法可以轮询链表以查看其中的任何WeakReferences是否已经死亡并且需要清除。

#5


0  

If identity comparison cannot be used then ConditionalWeakTable is not an option.

如果无法使用身份比较,则ConditionalWeakTable不是一个选项。

In this case I dare to suggest our implementation WeakTable.cs, and our description in the blog WeakTable.

在这种情况下,我敢于建议我们的实现WeakTable.cs,以及我们在博客WeakTable中的描述。

#1


24  

ConditionalWeakTable Class uses weak keys and automatically removes the key/value entry as soon as no other references to a key exist outside the table.

ConditionalWeakTable类使用弱键,并在表外没有其他对键的引用时自动删除键/值条目。

#2


5  

You'll need to write it yourself. It should be relatively straight forward, implementing the IDictionary interface and then storing the actual values as WeakReferences. You can then check the values on add/select to see if they're still alive.

你需要自己写。它应该相对简单,实现IDictionary接口,然后将实际值存储为WeakReferences。然后,您可以检查添加/选择的值,以查看它们是否仍然存在。

Pseudo code - won't really compile:

伪代码 - 不会真正编译:

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


0  

It is one thing to have WeakReferences to values, but I found that Dictionary keys can also be a source of memory leaks. Here is a bare bones implementation with WeakReference to keys:

将WeakReferences设置为值是一回事,但我发现Dictionary键也可能是内存泄漏的来源。这是一个使用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


0  

One problem with simply holding a dictionary of WeakReference objects is that there's no way, short of enumerating the entire dictionary, of removing from the Dictionary any WeakReference objects whose targets go out of scope.

简单地保存WeakReference对象字典的一个问题是,除了枚举整个字典之外,没有办法从Dictionary中删除任何目标超出范围的WeakReference对象。

It would be helpful if a WeakReference could include a delegate which would be invoked when the primary target went out of scope. As far as I know, there's no way to do that. If you don't mind adding another field and a little code to the objects you're storing within your "weak dictionary", I'd suggest creating what I call a "Finasposer" object, whose sole field is a MethodInvoker; when disposed, the MethodInvoker should be nulled out; the finalizer should Interlocked.Exchange() the MethodInvoker to null and--if its old value was non-null--invoke it. The object to be written in the dictionary should create a new Finasposer object, with a delegate that will cause the key to be removed from the dictionary when convenient.

如果WeakReference可以包含在主要目标超出范围时将调用的委托,将会很有帮助。据我所知,没有办法做到这一点。如果你不介意在你的“弱词典”中存储的对象中添加另一个字段和一些代码,我建议创建我称之为“Finasposer”的对象,其唯一字段是MethodInvoker;处置时,MethodInvoker应该被废除;终结器应该Interlocked.Exchange()将MethodInvoker设置为null,如果其旧值为非null,则调用它。要在字典中写入的对象应该创建一个新的Finasposer对象,其中一个委托将在方便时将该键从字典中删除。

Note that the neither the finalizer nor any delegate invoked thereby should never directly manipulate the dictionary, nor do anything that would require acquiring a lock. If the Finasposer holds a delegate, that delegate itself is guaranteed to be valid when Finalize executes, but the object attached to the delegate, and any objects referenced thereby, may be in unexpected states. It should be safe, however, for the Finasposer-called method to add to a linked list a reference to the object that went out of scope. The Dictionary's Add, Remove, and other methods could poll the linked list to see if any of the WeakReferences therein had died and needed to be cleaned out.

请注意,终结器和由此调用的任何委托都不应该直接操作字典,也不应该做任何需要获取锁定的事情。如果Finasposer持有委托,则该委托本身在Finalize执行时保证有效,但附加到委托的对象以及由此引用的任何对象可能处于意外状态。但是,对于Finasposer调用的方法来说,添加到链表中的对超出范围的对象的引用应该是安全的。 Dictionary的Add,Remove和其他方法可以轮询链表以查看其中的任何WeakReferences是否已经死亡并且需要清除。

#5


0  

If identity comparison cannot be used then ConditionalWeakTable is not an option.

如果无法使用身份比较,则ConditionalWeakTable不是一个选项。

In this case I dare to suggest our implementation WeakTable.cs, and our description in the blog WeakTable.

在这种情况下,我敢于建议我们的实现WeakTable.cs,以及我们在博客WeakTable中的描述。