直接设置字典元组值

Setting dictionary tuple values directly

在以下情况下是否可以做类似的事情:dictTupleTest[key].Item1 = toggle;

Dictionary<int, (bool, bool)> dictTupleTest = new Dictionary<int, (bool, bool)>();
var key = 3;
var toggle = false;

dictTupleTest.Add(key, (true, false));

//This works
dictTupleTest[key] = (toggle, dictTupleTest[key].Item2);

//While this gives an error
dictTupleTest[key].Item1 = toggle;

错误:Error CS1612: Cannot modify the return value of 'Dictionary<int, (bool, bool)>.this[int]' because it is not a variable.

或者有更好的方法吗?

元组是不可变的;它存储在字典中的事实是无关紧要的。你会得到同样的错误:

var x = dictTupleTest[key];
x.Item1 = toggle;

如果您想更改其中一个值,请不要使用元组 - 使用可变 class。否则,你这样做的方式是合适的(保留第二个值)。

编辑 -

感谢 Theodor Zoulias 指出我的推理有缺陷。元组是可变的,但出于某种原因(我不确定为什么),您不能更改内联字典访问器的元组的 属性 。当您尝试对 return 值(如 dictTupleTest[key]++)使用突变运算符时,该错误更为常见,但我不明白为什么调用 属性 set 不应该'不允许。

在任何情况下,将结果分配给变量 有效:

dictTupleTest.Add(key, (true, false));
var x = dictTupleTest[key];
x.Item1 = false;

Console.WriteLine(dictTupleTest[key]);  // outputs (false, false)

从 .NET 6 开始,可以更新存储在 Dictionary<TKey,TValue> 中的可变 value-type,而无需对字典执行多次操作。新的 API 是 CollectionsMarshal.GetValueRefOrNullRef 方法:

Gets either a reference to a TValue in the Dictionary<TKey,TValue>, or a reference null if it does not exist in the dictionary.

可以这样使用:

using System.Runtime.InteropServices;
//...
CollectionsMarshal.GetValueRefOrNullRef(dictTupleTest, key).Item1 = toggle;

如果在字典中找不到 key,上面的代码将抛出一个 NullReferenceException。为了更好地控制,您可以像这样使用 ref local

using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
//...
ref var entry = ref CollectionsMarshal.GetValueRefOrNullRef(dictTupleTest, key);
if (Unsafe.IsNullRef(ref entry)) throw new KeyNotFoundException();
entry.Item1 = toggle;

CollectionsMarshal.GetValueRefOrNullRef API 不容易被发现,并且 this is intentional:

This is niche unsafe API that 99% of .NET developers should not ever use. We do not want to encourage people to use it just because of they can.