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?
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)
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 theDictionary<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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With