在C#中是否可能创建一个真正的弱键字典?

27

我正在尝试为C#创建一个真正的WeakKeyedDictionary<,>,但是我遇到了困难。

我意识到这是一个非平凡的任务,但似乎无法声明一个WeakKeyedKeyValuePair<,>(仅在键可达时GC才会跟随值引用),因此看起来似乎不可能。

我看到两个主要问题:

  1. 迄今为止我见过的所有实现都没有在收集键之后修剪值。想想看 - 使用这种字典的主要原因之一是防止这些值被保留(不仅仅是键!)因为它们是不可达的,但是这里它们仍然通过强引用指向。

    是的,如果在字典中添加/删除足够多的条目,它们最终会被替换,但如果您没有这样做呢?

  2. 没有一个假设的WeakKeyedKeyValuePair<,>(或者另一种只在键可达时告诉GC标记值的方法),任何引用其键的值都永远不会被收集。这在存储任意值时是一个问题。

问题1可以通过一种不太理想/麻烦的方式来解决:使用GC通知等待完成完整的GC,然后在另一个线程中修剪字典。这一点我有点满意。

但是问题2让我困惑。我意识到这很容易被“那就不要那么做”所解决,但是这个问题是否可能被解决呢?

1个回答

33
请查看 ConditionalWeakTable<TKey, TValue> Class

允许编译器动态地将对象字段附加到托管对象。

它实质上是一个字典,其中键和值都是WeakReference,只要键存在,该值就会存活。
请注意!此类不使用GetHashCodeEquals进行相等性比较,而是使用ReferenceEquals

3
真遗憾,它似乎依赖于内部的.NET魔法“DependentHandle”来实现...另外,它忽略了“.GetHashCode”和“.Equals”,最多只能算是一个次标准的字典:( 此外,如果没有访问“DependentHandle”的权限,问题现在就转移到了定义“WeakValuedDictionary<,>”上。我想这可能是我们所能接受的最接近的解决方案了。 - Mania
9
DependentHandle是CLR实现的ephemerons,没有GC的协助是不可能实现的。它应该像GCHandle一样公开。如果您不介意使用反射,可以将静态CLR方法转换为委托并实现自己的DependentHandle和WeakValuedDictionary。请注意仔细研究.NET参考源代码(它是公开的),或者使用一些反编译工具,因为它有棘手的竞争条件。 - Zarat
2
@Zarat:顺便说一下,我希望看到一个框架类InternTable<T>(带有IEqualityComparer<T>作为构造参数)。给定一个T的实例,确定表中是否已经存在一个与之相等的条目。如果是,则比较该实例;否则,将提供的实例存储在表中并返回它。如果实现得好,这样的表可以极大地提高嵌套不可变类型的性能,其中哈希码可以相当可靠地区分不相等的实例,但比较相等的实例很昂贵。 - supercat
1
@supercat:我觉得我们有点跑题了 :) 如果你想进一步讨论,可以通过邮件或我的博客进行。我发布了我的反射技巧,重新实现DependentHandle和一个新的Ephemeron类,类似于WeakReference对GCHandle的作用。 - Zarat
1
我一直关注着这个讨论。我建议你在其他地方继续讨论,并发布一个自问自答的问题,分享你的发现。请在这里提供一个链接,以便我可以继续关注这个话题。谢谢! - dtb
显示剩余24条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接