Question

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
    }     
}
Was it helpful?

Solution

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.

OTHER TIPS

This is an old question but I think it's worth noting that this doesn't work in Java 15:

public class Main
{
   final int testInt;

   public Main()
   {
      init();
   }

   private void init() 
   {
      testInt = 3;
   }
}

java: variable testInt not initialized in the default constructor

So it seems C# readonly and Java final behave the same way.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top