ebeeb ebeeb - 2 months ago 15
C# Question

is the ValueTuple structure only mutable as variable?

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

Answer

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 field.

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