有没有一种方法可以使用lambda表达式在ConcurrentDictionary.TryUpdate中进行更新?

13
我有一个简单的情景,想要更新一个已有项的值。只有AddOrUpdate方法提供了一个委托,可以更新旧值。然而,如果键不存在,我不想添加任何东西。另外,TryUpdate方法没有重载,可以获得旧值。是否有一种方法可以使用当前的 API 来实现它?
以下是我正在寻找的签名:
bool TryUpdate(TKey key, Func<TValue,TValue> updateValueFactory)

没有这样的方法。您是否考虑过在键不存在时添加一个特殊值,并将该值视为条目不存在时的相同处理方式? - dtb
@dtb 由于我正在为这个类编写一个包装器,所以不可能出现无效的键。我可以专门要求它,但那不是我的首选。 - Ufuk Hacıoğulları
实际上,如果你正在编写一个包装器,你可以存储包装值的项目,从而让你创建自己的特殊墓碑值。但这会增加每个使用的成本。 - Jon Hanna
1个回答

19
你必须准备好循环并可能调用Func多次(与使用一个GetOrAdd重载的情况相同)。这意味着,如果Func具有副作用,则从外部来看它将不会是原子的。实际上,Func不应该具有副作用,但它们总是会带来一些代价,因此不能忽略重复调用的可能性。
public static bool TryUpdate<TKey, TValue>(
  this ConcurrentDictionary<TKey, TValue> dict,
  TKey key,
  Func<TValue, TValue> updateFactory)
{
    TValue curValue;
    while(dict.TryGetValue(key, out curValue))
    {
        if(dict.TryUpdate(key, updateFactory(curValue), curValue))
            return true;
        // if we're looping either the key was removed by another thread,
        // or another thread changed the value, so we start again.
    }
    return false;
}
正如所说,因为它可以循环,只有在Func没有副作用的情况下,从外部观察时才是原子性的。
(编辑:删除一个潜在的快捷方式,因为它太危险了,通常不建议使用,并且很可能会让尝试使用它的人受到伤害。)

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