我在哪里可以找到使用弱引用的IDictionary
的良好实现?
字典应该只保留对值的弱引用,并最终清除死引用本身。
或者我应该自己写吗?
ConditionalWeakTable Class使用弱键,并在表外没有其他对键的引用时自动删除键/值条目。
你需要自己写。它应该相对简单,实现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 );
}
}
将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
}
简单地保存WeakReference对象字典的一个问题是,除了枚举整个字典之外,没有办法从Dictionary中删除任何目标超出范围的WeakReference对象。
如果WeakReference可以包含在主目标超出范围时将调用的委托,将会很有帮助。据我所知,没有办法做到这一点。如果你不介意在你的“弱词典”中存储的对象中添加另一个字段和一些代码,我建议创建我称之为“Finasposer”的对象,其唯一的字段是MethodInvoker;处置时,MethodInvoker应该被淘汰;终结器应该Interlocked.Exchange()将MethodInvoker设置为null,并且 - 如果其旧值为非null - 则调用它。要在字典中写入的对象应该创建一个新的Finasposer对象,其中一个委托将在方便时从密钥中删除密钥。
请注意,终结器和任何被调用的委托都不应该直接操作字典,也不应该做任何需要获取锁的事情。如果Finasposer持有委托,则该委托本身在Finalize执行时保证有效,但附加到委托的对象以及由此引用的任何对象可能处于意外状态。但是,对于Finasposer调用的方法来说,添加到链表的对超出范围的对象的引用应该是安全的。 Dictionary的Add,Remove和其他方法可以轮询链表以查看其中的任何WeakReferences是否已经死亡并且需要清除。
如果无法使用身份比较,则ConditionalWeakTable不是一个选项。
在这种情况下,我敢于建议我们的实施WeakTable.cs,以及我们在博客WeakTable中的描述。
这是我的并发弱(值)字典的版本:
public class WeakConcurrentDictionary<TKey, TValue> : IDictionary<TKey, TValue> where TValue : class
{
private readonly ConcurrentDictionary<TKey, WeakReference<TValue>> _internalDictionary =
new ConcurrentDictionary<TKey, WeakReference<TValue>>();
public TValue this[TKey key]
{
get
{
if (_internalDictionary.TryGetValue(key, out var weakReference) &&
weakReference.TryGetTarget(out var value))
return value;
return null;
}
set
{
_internalDictionary.TryAdd(key, new WeakReference<TValue>(value));
}
}
public ICollection<TKey> Keys => _internalDictionary.Keys;
public ICollection<TValue> Values => _internalDictionary.Values
.Select(_ => _.GetTarget())
.Where(_ => _ != null)
.ToList();
public int Count => _internalDictionary.Count;
public bool IsReadOnly => false;
public void Add(TKey key, TValue value)
{
Purge();
if (!_internalDictionary.TryAdd(key, new WeakReference<TValue>(value)))
{
throw new InvalidOperationException("Key already existing");
}
}
public void Add(KeyValuePair<TKey, TValue> item)
{
throw new NotSupportedException();
}
public void Clear()
{
_internalDictionary.Clear();
}
public bool Contains(KeyValuePair<TKey, TValue> item) => _internalDictionary.TryGetValue(item.Key, out var weakReference) &&
weakReference.GetTarget() == item.Value;
public bool ContainsKey(TKey key) => _internalDictionary.TryGetValue(key, out var weakReference) &&
weakReference.IsAlive();
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
Purge();
_internalDictionary
.Select(_ => new KeyValuePair<TKey, TValue>(_.Key, _.Value.GetTarget()))
.Where(_ => _.Value != null)
.ToList()
.CopyTo(array, arrayIndex);
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
Purge();
return _internalDictionary
.Select(_ => new KeyValuePair<TKey, TValue>(_.Key, _.Value.GetTarget()))
.Where(_ => _.Value != null)
.GetEnumerator();
}
public bool Remove(TKey key)
{
return _internalDictionary.TryRemove(key, out var weakReference);
}
public bool Remove(KeyValuePair<TKey, TValue> item)
{
throw new NotSupportedException();
}
public bool TryGetValue(TKey key, out TValue value)
{
value = null;
if (_internalDictionary.TryGetValue(key, out var weakReference))
{
value = weakReference.GetTarget();
}
return value != null;
}
IEnumerator IEnumerable.GetEnumerator()
{
Purge();
return GetEnumerator();
}
public void Purge()
{
foreach (var itemToRemove in _internalDictionary
.Select(_ => new KeyValuePair<TKey, TValue>(_.Key, _.Value.GetTarget()))
.Where(_ => _.Value == null))
{
_internalDictionary.TryRemove(itemToRemove.Key, out var weakReference);
}
}
}
public static class WeakReferenceExtensions
{
public static bool IsAlive<T>([NotNull] this WeakReference<T> weakReference) where T : class =>
weakReference.TryGetTarget(out var target);
public static T GetTarget<T>([NotNull] this WeakReference<T> weakReference, T defaultValue = default(T)) where T : class
{
if (!weakReference.TryGetTarget(out T target))
return defaultValue;
return target;
}
}
并且测试证明实际上丢弃了对值的引用:
[TestMethod]
public void TestWeakDictionary()
{
var weakDict = new WeakConcurrentDictionary<string, TestItem>();
{
var testItem = new TestItem();
weakDict.Add("testitem", testItem);
Assert.AreEqual(1, weakDict.Count);
Assert.AreSame(testItem, weakDict["testitem"]);
}
GC.Collect();
Assert.IsNull(weakDict["testitem"]);
weakDict.Purge();
Assert.AreEqual(0, weakDict.Count);
}
一些说明: