Alex Rivera | Logout

const, readonly and mutable value types

Asked 2012-01-24T09:47:28.860
23

I'm continuing my study of C# and the language specification and Here goes another behavior that I don't quite understand:

The C# Language Specification clearly states the following in section 10.4:

The type specified in a constant declaration must be sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, string, an enum-type, or a reference-type.

It also states in section 4.1.4 the following:

Through const declarations it is possible to declare constants of the simple types (ยง10.4). It is not possible to have constants of other struct types, but a similar effect is provided by static readonly fields.

Ok, so a similar effect can be gained by using static readonly. Reading this I went and tried the following code:

static void Main()
{
    OffsetPoints();
    Console.Write("Hit a key to exit...");
    Console.ReadKey();
}

static Point staticPoint = new Point(0, 0);
static readonly Point staticReadOnlyPoint = new Point(0, 0);

public static void OffsetPoints()
{
    PrintOutPoints();
    staticPoint.Offset(1, 1);
    staticReadOnlyPoint.Offset(1, 1);
    Console.WriteLine("Offsetting...");
    Console.WriteLine();
    PrintOutPoints();
}

static void PrintOutPoints()
{
    Console.WriteLine("Static Point: X={0};Y={1}", staticPoint.X, staticPoint.Y);
    Console.WriteLine("Static readonly Point: X={0};Y={1}", staticReadOnlyPoint.X, staticReadOnlyPoint.Y);
    Console.WriteLine();
}

The output of this code is:

Static Point: X=0;Y=0

Static readonly Point: X=0;Y=0

Offsetting...

Static Point: X=1;Y=1

Static readonly Point: X=0;Y=0

Hit a key to exit...

I really expected the compiler to give me some kind of warning about mutating a static readonly field or failing that, to mutate the field

c#
Edit
Report

2 Answers

11

Eric Lippert explains what's going on here:

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

Oh and just to stress on the evilness of mutable structs, here is his conclusion:

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

answered 2012-01-24T10:03:19.230
5

The compiler simply doesn't have enough information available about a method to know that the method mutates the struct. A method may well have a side-effect that's useful but doesn't otherwise change any members of the struct. If would technically be possible to add such analysis to the compiler. But that won't work for any types that live in another assembly.

The missing ingredient is a metadata token that indicates that a method doesn't mutate any members. Like the const keyword in C++. Not available. It would have be drastically non-CLS compliant if it was added in the original design. There are very few languages that support the notion. I can only think of C++ but I don't get out much.

Fwiw, the compiler does generate explicit code to ensure that the statement cannot accidentally modify the readonly. This statement

staticReadOnlyPoint.Offset(1, 1);

gets translated to

Point temp = staticReadOnlyPoint;   // makes a copy
temp.Offset(1, 1);

Adding code that then compares the value and generates a runtime error is also only technically possible. It costs too much.

answered 2012-01-24T10:14:46.343

Your Answer