C# readonly vs Java final

14,234

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.

Share:
14,234
MgSam
Author by

MgSam

I am a full time software developer that works primarily with C#/.NET, TypeScript/JavaScript, HTML/CSS, XAML, and T-SQL.

Updated on June 04, 2022

Comments

  • MgSam
    MgSam almost 2 years

    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
        }     
    }
    
  • MgSam
    MgSam about 11 years
    @Virtlink Thank you for your answer. It's nice that not everyone just looks for excuses to close things.
  • ssss
    ssss over 10 years
    The first paragraph is wrong. You can modify a readonly field via Reflection, using only safe C# and verifiable IL. You can even change the value of string.Empty that way. Perhaps you meant trusted code, which is a completely different concept from verifiable IL. Untrusted code cannot use (most of) Reflection, but verifiable IL can.
  • Daniel A.A. Pelsmaeker
    Daniel A.A. Pelsmaeker over 10 years
    @Timwi You are correct, but when you bring reflection into the picture, all bets are off. I didn't mention it as it is not relevant to the question. That makes my first paragraph not wrong, at most incomplete.
  • Caffé
    Caffé about 9 years
    The curious thing is that Java final and C# readonly, for the sake of this question, work exactly in the same way: you cannot assign value to them anywhere in the class but only in the constructor or in its declaration
  • Daniel A.A. Pelsmaeker
    Daniel A.A. Pelsmaeker about 9 years
    @Caffé: That's only true for C#. In Java you can assign the value anywhere, but only once. You'd usually want to do that in the constructor for sanity's sake, but that's not required. You will get an exception if you try to assign it a second time, no matter where you do that.
  • Caffé
    Caffé about 9 years
    @Virtlink What java compiler have you been using? Check out the question's second example not working in Java: ideone.com/iq1EbV.
  • granadaCoder
    granadaCoder over 5 years
    "only by the constructor" is great for constructor based dependency injection.