Public readonly string vs. public readonly property?

I got an email asking me a question, and I thought it would make a good blog post. It has all the things that make a good Hollywood movie - Conflict, tradeoffs, and a grisly result if you choose wrong.

So, the question is, "Given the choice between a public readonly string or a public readonly property, which one is better and why?"

Just to make sure we're all on the same page, the public readonly string looks like this:

    class ThingOne
    {
        public readonly string Value;
        public ThingOne(string value) { this.Value = value; }
    }

and the public readonly property looks like this:

    class ThingTwo
    {
        private string value; 
        public ThingTwo(string value) { this.value = value; }
        public string Value { get { return this.value; } }
    }

So, what are the good points of each of these implementations?

The readonly one is simpler to read and understand and simpler to maintain (think of the differences in code with 10 of these in each class). It also has the advantage of being more precise, and by that I mean that the intent of the developer is crystal-clear; Value is something that is set when the instance is constructed, and never modified afterwards. That's not true with the property version - it could be modified by setting value in some other method, or adding a set accessor.

The property one provides the isolation that properties do - the implementation can be changed underneath without requiring the caller to change anything.

Those are the tradeoffs as I see them. Which one to use? Well, I think it depends on two factors:

1) How sure are you that this thing is truly readonly.

2) The pain if you're wrong.

Being less than convinced about my prescient abilities - and such abilities of developers in general - I think that #2 is the factor that you really need to consider. If the pain will be bad, you go with the properties approach. If it won't be bad, you go with the readonly approach. But, if you're really really sure that it's readonly and will always be readonly, then you can forget about the pain.

As for the pain potential, it depends mostly on what you're building and how your project is architected.

The first question is whether the class is visible outside of your assembly. If it isn't, then the pain of making a change is minimal, and I would prefer readonly because of the advantages I listed.

If it's visible outside the assembly, then the next question is "would we ever need to be update to update this assembly separately from its clients?"  In other words, do you have servicing requirements?

My original question here was whether the class was used outside your group, but it really comes down to build and deployment scenarios. If your group is the sole user of the assembly or all referenced assemblies are shipped as part of an update, then I would continue to prefer readonly.

If, however, this class is an externally-visible class and you think that it might need to be serviced (even if you don't do that sort of thing now), then you need the isolation that properties provide.

My own thinking in this area has certainly evolved over time. The more code that I work with, the more I think that premature generalization ranks right up there with premature optimization in the seven deadly sins of programming. Code that is more complex than it needs to be takes longer to write, and you pay the complexity tax every time somebody needs to work with the code. The difference between a class with 10 public fields and one private/property and one with 11 properties, 10 of which are trivial is substantial.

You may have noticed that I didn't mention efficiency in this post until now. Because of the inlining of properties, there typically isn't any difference, but even if that wasn't the case, I wouldn't consider it a factor in my decision. I'm convinced that a group that writes clean and simple code, tracks perf, and optimizes as appropriate will end up with better perf than a group that tries to code performant code along the way. Simple code is quicker to write and easier to optimize later if necessary.

So, that's what I think. Agree? Disagree? Want to know what the other 5 deadly sins are?

Tom, thanks for the question.