代码之家  ›  专栏  ›  技术社区  ›  Konstantin Spirin

.NET中弱字典的良好实现

  •  27
  • Konstantin Spirin  · 技术社区  · 16 年前

    在哪能找到 IDictionary 哪一个在内部使用弱引用?

    字典应该只保存对值的弱引用,并最终清除死引用。

    还是我自己写?

    5 回复  |  直到 9 年前
        1
  •  28
  •   madrang    11 年前

    ConditionalWeakTable Class 使用弱键并在表外不存在对键的其他引用时自动删除键/值项。

        2
  •  5
  •   Sebastian Krysmanski XDS    9 年前

    你得自己写。它应该是相对直接的,实现IDictionary接口,然后将实际值存储为weakreferences。然后,您可以检查“添加/选择”上的值,以查看它们是否仍处于活动状态。

    伪代码-不会真正编译:

    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
  •   Kyle Lahnakoski    15 年前

    对值进行weakreferences是一回事,但我发现字典键也可能是内存泄漏的来源。下面是一个简单的实现,它引用了键:

    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
  •   supercat    15 年前

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

    如果一个weakreference可以包含一个委托,当主目标超出范围时将调用该委托,这将是很有帮助的。据我所知,没有办法做到这一点。如果您不介意向“弱字典”中存储的对象添加另一个字段和一点代码,我建议创建一个我称之为“finasposer”的对象,其唯一字段是一个MethodInvoker;释放后,MethodInvoker应为空;终结器应为互锁.exchange()MethodInvoker为空,并且--如果它的ld值非空--调用它。要在字典中写入的对象应该创建一个新的finasposer对象,该对象带有一个委托,该委托将在方便时从字典中删除键。

    请注意,终结器和由此调用的任何委托都不应直接操作字典,也不应执行任何需要获取锁的操作。如果finasposer持有委托,则该委托本身在finalize执行时是有效的,但附加到委托的对象及其引用的任何对象可能处于意外状态。但是,对于finasposer调用的方法来说,将对超出范围的对象的引用添加到链接列表应该是安全的。字典的添加、删除和其他方法可以对链接列表进行轮询,以查看其中的任何weakerence是否已经消失,是否需要清除。

        5
  •  0
  •   Vladimir Nesterovsky    12 年前

    如果不能使用标识比较,则ConditionalWeakTable不是选项。

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