MgSam MgSam - 1 month ago 22
C# Question

C# readonly vs Java final

In Java,

final
means a variable can only be assigned to once, but that assignment can happen anywhere in the program. In C#,
readonly
means a field can only be assigned in a constructor, which, IMO, is significantly less useful.

As we all know, C# was heavily influenced by Java design, but this difference has always puzzled me as being very odd. Does anyone know if there's a technical reason in the CLR that resulted in the less-useful behavior of C#'s
readonly
vs Java's
final
?

EDIT:

In response to the comments; I'd like to point out that I am well aware of the benefits of immutability, and I use it all over the place. I believe
readonly
is less useful than Java because of this:

public class Foo
{
private readonly int _bar;

Foo()
{
_bar = 5;
}
}


Whoops, I actually need to initialize that value in a helper method!

public class Foo
{
private readonly int _bar;

Foo()
{
initialize()
}

private void initialize()
{
_bar = 5; //Can't compile because of semantics of readonly
}
}

Answer

There is a technical reason for the behavior of readonly: in the created assembly's metadata the field is marked with the initonly attribute that will ensure the field is not modified outside a constructor.1 However, while unverifiable, by taking the address of the readonly field it is still possible to change its value. Verifiable IL and C# will not allow you to do this.

At compile time it is impossible to enforce this for all methods, since the compiler would have to analyze all possible orders in which methods could be called. At runtime it would probably be a burden on the CLR and negative for performance if it had to check every field write whether it has been written to before. Instead, it is safer that C# and the CLR just don't allow the field to be assigned a value anywhere except in the carefully analyzed scope of a constructor.

In my opinion this does not make the readonly keyword any less valuable. I use it all over the place for fields whose value is provided only by the constructor (e.g. creating a list, or storing a constructor argument). C# will ensure that I won't change the field after that ever again, ensuring that I cannot accidentally set it to null or anything.

1) Thanks to Eric Lippert for pointing this out.

Comments