Mutating value types – Part 1

Take a look at the following short snippet of code.

  1: using System;
  3: struct S
  4: {
  5:     public int X;
  6: }
  8: class C
  9: {
 10:     /* More code here */
 11: }
 13: class Test
 14: {
 15:     public static void Main()
 16:     {
 17:         C c = new C();
 18:         c.S.X = 1;
 19:     }
 20: }

Without knowing the type definition of C, can you tell whether the code will compile, much less work?

Turns out that you can’t. It depends on whether the S in c.S is defined as a field or a property.


class C
    public S S;


class C
    public S S { get; private set; }

The first type definition will compile, the second won’t (error CS1612: Cannot modify the return value of ‘C.S’ because it is not a variable)

Can you guess why?

Let’s assume the compiler does allow the second type definition to compile. Would you expect the value of X in the instance of S inside C to change? That is, what would be the output of

public static void Main()
    C c = new C();
    c.S.X = 1;


If you’re expecting it to be 1, then you have just broken the value semantics of a struct, S might as well have been defined as a class. The above code is logically identical to

public static void Main()
    C c = new C();
    S temp = c.S;
    temp.X = 1;


Because S is a struct, the property getter always returns a copy (temp), and changing the copy will have no effect on the original instance. With c.S.X = 1, you can’t access the copy either, so the only effect of executing that statement will be to make the poor developer’s eyes go wide as he steps through the code in the debugger, wondering why a simple field assignment refuses to work.

So, the C# compiler is being helpful here by not allowing this kind of code to compile.

Right, so why does it compile if S is defined as a field rather than a property? We’ll see why in the next post. Meanwhile, feel free to post why you think it works.

Leave a Reply

Your email address will not be published. Required fields are marked *