Const Strings – a very convenient way to shoot yourself in the foot

If you want to have a static readonly string, you have two options:

public static class MyStringTestClass { public static readonly string StaticReadonly = "Static ReadOnly String"; public const string ConstString = "Const String"; }

The difference is subtle at first: StaticReadonly is like a normal field that gets initialized through a static constructor, while ConstString is "hardcoded". If you look the Assembly at it in Reflector, it looks like this:

public static class MyStringTestClass { // Fields public const string ConstString = "Const String"; public static readonly string StaticReadonly; // Methods static MyStringTestClass() { StaticReadonly = "Static ReadOnly String"; } }

So the obvious difference is that a static readonly string is initialized in the constructor (which also means that you can set it dynamically a construction), while a const string is really carved in stone. But the real difference is more subtle. Let's create a second assembly, reference the first one and add a class:

public class MyStringTestConsumer { public void TestMethod() { string sro = MyStringTestClass.StaticReadonly; SomeOtherFunction(sro); string sc = MyStringTestClass.ConstString; SomeOtherFunction(sc); } public void SomeOtherFunction(string input) { // Dummy function to prevent "string sc" // being optimized away by the compiler } }

Compile this second assembly, load it into reflector, and look at TestMethod:

public void TestMethod() { string sro = MyStringTestClass.StaticReadonly; this.SomeOtherFunction(sro); string sc = "Const String"; this.SomeOtherFunction(sc); }

As you see, A const string is not a reference to something. While a static readonly string is still a reference to a field in a class which will be resolved only at runtime, a const string is actually "copy/pasted" by the compiler. What does that mean? At first, it means a theoretical performance increase, because no lookup will take place, hence it is usually recommended by FxCop.

But there is one big caveat with it: Say you have multiple assemblies, one that provides the const and one that consumes it. What happens if you change the two string in the "provider.dll" without touching the consumer.dll? The MyStringTestClass.StaticReadonly will point to the new reference, but the const string will not change, because it had been literally inserted. You will need to recompile the consumer.dll to have the string replaced with the new one.

I have just been bitten by this. I have two provider.dlls, one for a test and one for a live environment, but only one consumer.dll. I accidentally declared a string as const. Needless to say, deploying the consumer.dll to a live environment led to some... interesting... results. So yeah: Consts are really useful, but sometimes a static readonly field works better.

PS: I think the same applies to other value-types like int as well, but I never shot myself in the foot with an int.