Mutating readonly structs

Consider this program which attempts to mutate a readonly mutable struct. What happens?

using System;
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();    
    Console.WriteLine(t.m.Mutate());  
    Console.WriteLine(t.m.Mutate());   
    Console.WriteLine(t.m.Mutate());     
  } 
}

There are a number of things this program could do. Does it:

  1. Print 1, 2, 3 — because m is readonly, but the “readonly” only applies to
    m, not to its contents.
  2. Print 0, 0, 0 — because m is readonly, x cannot be changed. It always has its default value of zero.
  3. Throw an exception at runtime, when the attempt is made to mutate the contents of a readonly field.
  4. Do something else

?

People are frequently surprised to learn that the answer is (4). This prints 1, 1, 1.

Why?

Because, remember, accessing a value type gives you a copy of the value. When you say t.m, you get a copy of whatever is presently stored in m. m is immutable, but the copy is not. The copy is then mutated, and the value of x in the copy is returned. But m remains untouched.

The relevant section of the specification is 7.5.4, which states that when resolving “E.I” where E is an object and I is a field…

…if the field is readonly and the reference occurs outside an instance constructor of the class in which the field is declared, then the result is a value, namely the value of the field I in the object referenced by E.

The important word here is that the result is the value of the field, not the variable associated with the field. Readonly fields are not variables outside of the constructor. (The initializer here is considered to be inside the constructor; see my earlier post on that subject.)

Great. What about that second dot, as in .Mutate()?  We look at section 7.4.4 to find out how to invoke E.M():

If E is not classified as a variable, then a temporary local variable of E’s type is created and the value of E is assigned to that variable. E is then reclassified as a reference to that temporary local variable. The temporary variable is accessible as this within M, but not in any other way. Thus, only when E is a true variable is it possible for the caller to observe the changes that M makes to this.

And there you go. Value semantics are tricky!

This is yet another reason why mutable value types are evil. Try to always make value types immutable.

Advertisements

3 thoughts on “Mutating readonly structs

  1. Pingback: immutable value types | BlogoSfera

  2. Mutable value types aren’t evil, the C# designers merely added an utterly confusing dogmatic rule. Religion and code do not mix!

  3. “accessing a value type gives you a copy of the value. When you say t.m, you get a copy of whatever is presently stored in m”

    @Eric, kindly correct me if I am wrong. According to the above statement “t.m” should give a copy irrespective of whether it’s readonly, right? But when I removed readonly keyword the result is 1,2,3 as expected. If “t.m” is really giving a copy it should be 1,1,1 right?
    I tested it in C# 6.0.

    Am I missing something here?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s