Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting dictionary tuple values directly

Is it possible to do something similar to this: dictTupleTest[key].Item1 = toggle; in the following situation?

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;

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

Or is there a better way to do it?

like image 738
mrxan Avatar asked Oct 16 '25 03:10

mrxan


2 Answers

Tuples are immutable; the fact that it's stored in a dictionary is irrelevant. You'd get the same error with:

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

If you want to change one of the values, then don't use a tuple - use a mutable class. Otherwise, the way you're doing it is appropriate (keeping the second value).

EDIT -

Thanks to Theodor Zoulias for pointing out that my reasoning was flawed. The tuple is mutable, but for some reason (I'm not sure why), you can't change a property of the tuple inline with a dictionary accessor. That error is more common when you try to use mutation operators on a return value (like dictTupleTest[key]++), but I don't see why calling a property set shouldn't be allowed.

In any case, assigning the result to a variable does work:

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

Console.WriteLine(dictTupleTest[key]);  // outputs (false, false)
like image 86
D Stanley Avatar answered Oct 18 '25 19:10

D Stanley


Starting from .NET 6, it is possible to update a mutable value-type stored in a Dictionary<TKey,TValue> without doing more than one operations on the dictionary. The new API is the CollectionsMarshal.GetValueRefOrNullRef method:

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

It can be used like this:

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

In case the key is not found in the dictionary, the above code will throw a NullReferenceException. For better control you can use a ref local like this:

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;

The CollectionsMarshal.GetValueRefOrNullRef API is non easily discoverable, and -- jkotas commented on Jul 15, 2021">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.

like image 25
Theodor Zoulias Avatar answered Oct 18 '25 20:10

Theodor Zoulias