Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 ValueTuples 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

like image 548
Michael Schnerring Avatar asked Jan 04 '23 01:01

Michael Schnerring


1 Answers

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

like image 115
Alexei Levenkov Avatar answered Feb 02 '23 03:02

Alexei Levenkov