我是
ConditionalWeakTable
的忠实粉丝。 真的非常棒。 它本质上允许您将一个引用关联/附加/配对到另一个引用,同时保持线程安全。
它为我解答了很多问题。 不幸的是,我似乎一直遇到一个问题,并且无法找到一个好的答案。
假设我有一个方法(由下面的测试方法表示),其中我使用
ConditionalWeakTable
,但我不需要对键使用单个引用,而是需要组合两个项目并将其作为键。 下面通过 Key
对象显示了这一点(这只是一个扩展的 Tuple
,用于演示目的和简洁性):
[Fact]
public void TupleKey()
{
var first = new object();
var table = new ConditionalWeakTable<Key, object>();
var context = table.GetValue( new Key( first, new object() ), key => new object() );
// .. table has one entry here.
GC.Collect();
GC.WaitForPendingFinalizers();
Debugger.Break(); // Table is empty here.
}
class Key : Tuple<object, object>
{
public Key( object item1, object item2 ) : base( item1, item2 ) {}
}
当调用
Debugger.Break
时,ConditionalWeakTable
为空,即使测试方法中存在对 Key
部分之一的活动引用。 这是有道理的,因为在测试中找不到对 Key
的引用。
但是,由于存在对
Key
对象的某一部分的一个活动引用,因此我希望 ConditionalWeakTable
中的条目也保持活动状态,并保持活动状态,直到声明其所有子级/内部引用。
(在考虑这个和可能的解决方案时,如果有一个 WeakHashSet
可以以某种方式用作
Dictionary<>
的钥匙,那就太棒了,但从一些搜索来看,似乎没有
WeakHashSet
存在于.NET 中。)希望这能概述我的困境。 我基本上想使用
ConditionalWeakTable
(或某些等效项)来存储关联的值(作为键/值条目),但我不想使用一个(弱)引用作为条目的键,我想使用两个(或更多)。我希望实现的目标是否可能(即无需编写大量自定义代码)?
这个问题相关,我终于找到了一个解决这个问题的答案,那个答案很好。 您可以阅读答案了解更多详情。
关于这个问题。 这归结为更精致/聪明/智能地使用“钥匙”来使用ConditionalWeakTable
。 就我而言,我正在努力解决密钥的三个部分(实例、方法和参数)。 我设法使用
Delegate
(基本上是实例和方法的参考元组)组合前两个,然后将其与使用
Dictionary<object[], object>
的 StructuralEqualityComparer
配对。 哇啦。 问题解决了。
ConditionalWeakTable
。例如,对于接受三个参数并根据所有三个键的引用进行记忆的 F# 记忆函数,这似乎有效:
module Memoize =
/// Memoizes the specified function using reference equality on the input arguments.
/// The result is cached for the lifetime of the key.
///
/// Don't call with additional arguments as ad-hoc tuples or records,
/// since these will never be reference equal.
let refEq3<'k1, 'k2, 'k3, 'v
when 'k1: not struct and 'k2: not struct and 'k3: not struct and 'v: not struct>
(f: 'k1 -> 'k2 -> 'k3 -> 'v)
=
let cache = ConditionalWeakTable<'k1, ConditionalWeakTable<'k2, ConditionalWeakTable<'k3, 'v>>>()
fun k1 k2 k3 ->
let inner1 = cache.GetOrCreateValue(k1)
let inner2 = inner1.GetOrCreateValue(k2)
match inner2.TryGetValue(k3) with
| true, v -> v
| false, _ ->
let v = f k1 k2 k3
inner2.Add(k3, v)
v