is the ValueTuple structure only mutable as variable?

c#, immutability, mutability, valuetuple

Solution

There are three cases when one can change mutable struct and see result:

- local variable: `MyStruct s = new MyStruct(); s.Prop = 4;`

- field of another type: `class MyType { public MyStruct S;} ... myType.S.Prop = 4;`

- element of array: `MyStruct[] myArray =...; myArray[3].Prop = 4;`.

Why code in the post did not detect change - code used property and not a field.

Note that `List<MyStruct>` does not allow modification because indexer (`this[..]`) returns copy of an item (as there is no support returning reference like C++ does).

Problem

I played around with the `ValueTuple` structure and tried to implement an immutable composite key. The key is composed of value types. I tried to break the following implementation with some unit tests, so far without success. Am I missing something? Also this is just out of curiosity, I want to get to `ValueTuple`s and it's limitations before the release of .NET 4.7. So far my understanding of a `ValueTuple` is that it is only mutable as a variable but not as a field or property. Not sure what "mutable" means here though. Does altering a `ValueTuple` instance actually create a new `ValueTuple` (like it's common knowledge that strings are "immutable" but actually reference types)? from this answer `System.ValueTuple` isn't only a `struct`, it's a mutable one, and one has to be careful when using them as such. Think what happens when a class holds a `System.ValueTuple` as a field. Here my implementation and tests ``` public interface IHaveCompositeKey { (Guid id, string name) Key { get; } } public class ImmutableKey : IHaveCompositeKey { public (Guid id, string name) Key { get; } public ImmutableKey((Guid id, string name) key) => Key = key; public override int GetHashCode() => Key.GetHashCode(); public override bool Equals(object obj) { var a = obj as ImmutableKey; return a != null && Key.Equals(a.Key); } } [TestFixture] public class KeyTests { [Test] public void Test1() // passes { var key = (Guid.NewGuid(), "Foo"); var a = new ImmutableKey(key); var b = new ImmutableKey(key); Assert.IsTrue(a.Equals(b)); Assert.IsTrue(a.GetHashCode().Equals(b.GetHashCode())); } [Test] public void Test2() // passes { (Guid id, string name) key = (Guid.NewGuid(), "Foo"); var a = new ImmutableKey(key); key.name = "Bar"; // mutable var b = new ImmutableKey(key); Assert.IsFalse(a.Equals(b)); Assert.IsFalse(a.GetHashCode().Equals(b.GetHashCode())); } [Test] public void Test3() // does not compile { var key = (Guid.NewGuid(), "Foo"); var a = new ImmutableKey(key); // compilation error a.Key.name = "Bar"; // immutable var b = new ImmutableKey(a.Key); Assert.IsFalse(a.Equals(b)); Assert.IsFalse(a.GetHashCode().Equals(b.GetHashCode())); } } ``` error: Cannot modify the return value of 'ImmutableKey.Key' because it is not a variable

Original source

Related problems