devuxer
devuxer

Reputation: 42364

How best to implement publicly accessible constants in C#

There seem to be three choices for implementing publicly accessible constants in C#. I'm curious if there are any good reasons 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.

Upvotes: 10

Views: 2821

Answers (6)

user370770
user370770

Reputation:

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 (as an example, a version number may be made into a const, and could change) or for values that absolutely will never change as far as the code that references it is concerned.

Upvotes: 1

user315772
user315772

Reputation:

const members are class members, not instance members (in other words, const implies static).

Upvotes: 2

pelazem
pelazem

Reputation: 1382

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.

Upvotes: 5

John S.
John S.

Reputation: 2007

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.

Upvotes: 1

Jon Skeet
Jon Skeet

Reputation: 1500575

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.

Upvotes: 12

Hans Passant
Hans Passant

Reputation: 941515

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.

Upvotes: 2

Related Questions