tags:

views:

172

answers:

6

There seem to be three choices for implementing publicly accessible constants in C#. I'm curious if there are any good reason to choose one over the other or if it's just a matter of personal preference.

Choice 1 - private field plus property getter

private const string _someConstant = "string that will never change";

public string SomeConstant
{
    get { return _someConstant; }
}

Choice 2 - property getter only

public string SomeConstant
{
    get { return "string that will never change"; }
}

Choice 3 - public field only

public const string SomeConstant = "string that will never change";

Which do you recommend and why?


Update

Apparently, this has turned into a discussion of whether to use const or static readonly. Not exactly what I intended, but it did teach me that Choice 3 is definitely a bad idea because, if the value of the const changes in a future version, it requires all referencing assemblies to be recompiled.

However, I don't think anyone has really discussed Choice 2 yet. I'm still curious if there's any disadvantage with having just a getter that returns a value and nothing else.

+6  A: 

Consider

public static readonly string myVar = "something";

Reason: when you expose (and then consume elsewhere) a const, the const is embedded in the consuming type's metadata.

A public static readonly isn't, and since it's static readonly, it only costs you for instantiation once, and it is immutable like a const.

pelazem
The const is not embedded in the metadata. The problem is that the literal value of the const is embedded in the IL.
Hans Passant
why is that a problem?
thecoop
It's a problem for separate compilation. If you change the definition of the constant you will need to recompile (as opposed to just reload / re-JIT) any dependent assemblies.
Doug McClean
@thecoop: I updated my answer to explain why.
Hans Passant
@Hans Passant - thank you, that is what I meant but you expressed it more precisely.
pelazem
+2  A: 

The proper one is choice #4:

 public static readonly string SomeConstant = "string that might change in a new version";

Using a readonly field instead of a public const is important. The literal value of a const is compiled into the IL. If you change the const value and recompile one assembly that uses it, you'll now have a mismatch with other assemblies that use the const as well. Those other assemblies will still run with the old const value. Very hard to diagnose.

This cannot happen with a readonly field, the other assemblies will always read the updated value. If you use const, be sure to always make them private.

Hans Passant
Is there some reason I couldn't/shouldn't change the value of a `const` in a new version? I've certainly done it before without noticing any negative consequences.
DanM
Yes there is. The literal value of a const is embedded in the IL. You'll run into big trouble when you recompile only one library (say, for a bug fix). Other assemblies that uses the const will still run with the old value. A const should always be declared private to ensure that can never happen.
Hans Passant
Hans, well then why not go with Choice #1? The const is declared private. And why is static necessarily desirable? Maybe I'd like to access the `const` value through an instance of the class to allow for polymorphism.
DanM
No argument with that, but why write 5 lines of code when 1 will do?
Hans Passant
Hans, see edit to my comment.
DanM
Hans, also, if typing five lines of code is an issue, what's wrong with choice #2?
DanM
I don't think anybody would consider a virtual property a "polymorphic constant". Call it a property. The pattern is common.
Hans Passant
Hans, that's a fair point, but whatever you call it, Choice #2 is relatively compact, allows for polymorphism, and doesn't have the problem with the values getting written into the IL. Do you agree?
DanM
Hm, well, in theory I would argue that a `const` shouldn't change, even in future releases. In practice this is of course another matter (is a variable `version` a const for instance). (Philosophically I guess nothing is constant. :-) )
Patrick
+3  A: 

Dan, I can't post comments, but const members are class members, not instance members (in other words, const implies static).

Phong
Good point, Phong.
DanM
+6  A: 

Choices 1 and 2 are equivalent, really.

It seems to me there are really three different situations:

  • You know for certain that the string will never, ever change. In this case it's reasonable to make it const. (For example, Math.PI is const. That's not going to change any time soon.) There are some subtle memory implications in doing this over using static readonly, but they're very unlikely to affect you. You shouldn't do this if the value may change and you don't want to recompile all callers in that situation, for the reasons given elsewhere. Note that for many projects (particularly internal corporate ones) it's really not a problem to recompile all callers.

  • You think the string might change in future, but you know it will always be a constant within any one version. In this case, a public static readonly field is okay. Bear in mind that it's fine to do this with strings as they're immutable, but you shouldn't do this with any mutable types such as arrays. (Either expose immutable collections or use a property and return a new copy each time.)

  • You think the string might change, and it could even change within the lifetime of a program... for example, "the current date, formatted". In this case, use a public static read-only property (one with only a getter). Note that changing from a readonly field to a read-only property is a source-compatible change, but not a binary-compatible change - so if you have plumped for my second bullet but then need to change to the third, you need to recompile everything.

Jon Skeet
+1. Very helpful, Jon, thanks.
DanM
Hmm, I just tried your third option, but I'm getting "The modifier `readonly` is not valid for this item. The offending code was: `public static readonly string SomeConstant { get { return "SomeValue" } }`. Did you mean to say just `public static`?
DanM
In retrospect, I think you must have just meant "readonly" in the sense of "has a getter only, no setter", not to literally try to tag the property `readonly` :)
DanM
@DanM: Yes, I'll clarify with a hyphen :)
Jon Skeet
+2  A: 

If I could vote up - I would vote up Jon Skeet's answer. To add on to it your #1 and #2 are exactly identical as shown here in IL:

.method public hidebysig specialname instance string 
    get_SomeConstant() cil managed
{
  // Code size       11 (0xb)
  .maxstack  1
  .locals init ([0] string CS$1$0000)
  IL_0000:  nop
  IL_0001:  ldstr      "string that will never change"
  IL_0006:  stloc.0
  IL_0007:  br.s       IL_0009
  IL_0009:  ldloc.0
  IL_000a:  ret
} // end of method Class1::get_SomeConstant

Option #2:

.method public hidebysig specialname instance string 
    get_SomeConstant() cil managed
{
  // Code size       11 (0xb)
  .maxstack  1
  .locals init ([0] string CS$1$0000)
  IL_0000:  nop
  IL_0001:  ldstr      "string that will never change"
  IL_0006:  stloc.0
  IL_0007:  br.s       IL_0009
  IL_0009:  ldloc.0
  IL_000a:  ret
} // end of method Class2::get_SomeConstant

Now looking at option #3. Number 3 is very different from #1 and #2. The reason for this is, as previously stated, #3 is static since const is static. Now the real question would be to compare apples to apples in that what if #1 and #2 where static accessors? Then they would be more comparable to #3. Currently you would have to initialize a class for option 1 and 2, but not for #3. Thus there is an unnecessary initialization of an object in this case and you always want to use static whenever possible because of to avoid just that.

Now lets look at number 3 in the IL:

.field public static literal string SomeConstant = "string that will never change"

So, for efficiency, I would use #3. This is also what I have been taught by many talented peers over the years.

Now, to address the white elephant in the room. Readonly and const differ in that cont occur at compile time and readonly occurs at run time. Static readonly is initialized once, while nonstatic readonly is initialized once per instance. If, for example you are asking your question to do something like create a class of const strings for error messages that will never change then use option #3 and not readonly static or otherwise. Think of trying to initialize hundreds of error messages at run time instead of at compile time, you will see a noticeable performance difference. Also, since you state clearly that it is a "string that will never change" readonly should not even be considered in this case because "...it will never change". Const and ReadOnly have their places, but readonly is not for items that will never change and are known at compile time.

John
+1. Thanks, John. I should clarify that I meant the string will never change *during* runtime. It might change between versions.
DanM
+1  A: 

A property really seems like the better choice because the returned string then isn't embedded in the metadata. const are really meant for internal use (if they may change, eg. version numbers) or for values that absolutely will never change.

Mark