7

I am reading Eric Liperts' blog about Mutating Readonly Structs and I see many references here in SO to this blog as an argument why value types must be immutable. But still one thing is not clear, says that when you access value type you always get the copy of it and here is the example :

struct Mutable
{
    private int x;
    public int Mutate()
    {
        this.x = this.x + 1;
        return this.x;
    }
}

class Test
{
    public readonly Mutable m = new Mutable();
    static void Main(string[] args)
    {
        Test t = new Test();
        System.Console.WriteLine(t.m.Mutate());
        System.Console.WriteLine(t.m.Mutate());
        System.Console.WriteLine(t.m.Mutate());
    }
}

And the question is this why when I change the

public readonly Mutable m = new Mutable();

to

public Mutable m = new Mutable();

everything starts to work es expected.

Please can you explain more clear why Value Types must be immutable. I know that it is good for thread safety, but in this case same can be applied to reference types.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
NDeveloper
  • 1,837
  • 4
  • 20
  • 34
  • I wouldn't know how to answer this without just copying and pasting further quotes from the very blog post you've linked to. `m` being `readonly` is what makes `t.m` a value, which, not being a variable is then placed in a temporary variable, and it is this temporary variable that is `this` within the `Mutate` method. Take away `readonly`, and the remaining factors no longer apply, so `this` within `Mutate` can be `m`. – Damien_The_Unbeliever Jul 04 '11 at 09:15

4 Answers4

4

Structs with mutating methods behave strangely in several situations.

The example you already discovered is a readonly field. A defensive copy is necessary because you don't want to mutate a readonly field.

But also when used as properties. Once again an implicit copy happens, and only the copy is mutated. Even if the property has a setter.

struct Mutable
{
    private int x;
    public int Mutate()
    {
        this.x = this.x + 1;
        return this.x;
    }
}

Mutable property{get;set;}

void Main()
{
    property=new Mutable();
    property.Mutate().Dump();//returns 1
    property.Mutate().Dump();//returns 1 :(
}

This shows that mutating methods are problematic on structs. But it doesn't show that a mutable struct with either public fields or properties that have a setter is problematic.

CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
2

The thread-safety is a clear technical reason. It applies to value types as well as to reference types (see System.String).

The more general guideline "value types should be immutable" is different. It is about readability of code, and comes mainly from the confusion that mutable values can cause. This code snippet is just one example. Most people would not expect the 1,1,1 outcome.

H H
  • 263,252
  • 30
  • 330
  • 514
  • Yes but still if it is not readonly everything will work as expected. More general the question is not in that, but it is why value types suggested to be immutable ? – NDeveloper Jul 04 '11 at 09:29
  • @Ndev: I think I answered the more general (Why) question. @Damien has analyzed the code sample. – H H Jul 04 '11 at 09:32
  • but wouldn't the readability be the same as for ref types ? Or you mean this is the matter that they pass a reference so can be mutated .. – NDeveloper Jul 04 '11 at 09:38
  • @ndev, change `struct` to `class` and run the code again, with and without readonly. – H H Jul 04 '11 at 09:45
2

I don't know C# so I'll try to answer the 2nd part of your question.

Why value types must be immutable?

There are two types of objects from Domain Driven Design's point of view:

  • value objects/types - their identity is determined by their value (e.g. numbers: 2 is always 2 - an identity of number two is always the same, so 2 == 2 is always true)
  • entities (reference types) - they can consist of other value types and their identity is determined by their identity itself (e.g. people: even if there was man looking exactly like you, it wouldn't be you)

If value types were mutable, then imagine what could happen if it would be possible to change the value of the number two: 2 == 1 + 1 wouldn't be guarantied to be true.

See these links for more:

Community
  • 1
  • 1
mnicky
  • 1,308
  • 11
  • 24
  • While mutable valuetypes are problematic it's not as bad as it sounds in your answer. The problem typically isn't that you can mutate something you shouldn't, but that you accidentally mutate a temporary copy instead of what you wanted to mutate. – CodesInChaos Jul 04 '11 at 12:44
0

I think the tricky thing about that example is that one could argue it shouldn't be possible. You made an instance of Mutable read-only and yet you can change its value through the Mutate() function, therefore violating the concept of immutability, in a sense. Strictly speaking, however, it works because the private field x is not readonly. If you make one simple change in the mutable class then immutability will actually be enforced:

private readonly int x;

Then the Mutate() function will produce a compiler error.

The example shows clearly how copy-by-value works in the context of readonly variables. Whenever you call m you are creating a copy of the instance, as opposed to a copy of a reference to the instance -- the latter would occur if Mutable were a class instead of a struct.

Since everytime you call m you are calling 1) a copy of the instance, and 2) a copy of an instance that is read-only, the value of x is always going to be 0 at the time the copying takes place. When you call Mutate() on the copy it increments x to 1, which works because x itself is NOT readonly. But next time you call Mutate() you are still calling it on the original default value of 0. As he says in the article "m is immutable, but the copy is not". Every copy of the original instance will have x as 0 because the object being copied never changes whereas its copies can be changed.

Maybe that helps.

Sean Thoman
  • 7,429
  • 6
  • 56
  • 103