Why you should use static read-only fields over constants when dealing with strings
It’s really wonderful when you learn something new once in a while. As I was discussing with another .NET developer about the importance of knowing when to use constants and fields, he corrected me when I mistakenly told him that String.Empty was implemented as a constant. In fact, if you look inside the String class, you’ll actually see that String.Empty is not implemented as a constant, but as a field (a static read-only field to be precise).
As I was thinking about the reason behind this, something hit me: I should be conscious of my decision whether I should use a constant or a field when dealing with strings. The reason is simple: avoid unnecessary performance hits. Take note that the only valid reference type for a constant is either string or null because you cannot instantiate a reference type in compile-time, which is when constants are evaluated.
I made a simple test to verify my little theory, since I couldn’t find a good answer for it on the Web. The test consist of a class that declares both a constant string and a static read-only string field. In its entry method, I call various methods which iterate a certain number of times (10, 100, 1000, 10 000 and 100 000) and use either the constant or the static read-only field respectively. Here’s a sample code I used for this test:
class Program
{
private const string Constant = "Constant";
private static readonly string field = "field";
private static void Main(string[] args)
{
// 10 iterations
DoConstant10();
DoField10();
// 100 iterations
DoConstant100();
DoField100();
// 1000 iterations
DoConstant1000();
DoField1000();
// 10000 iterations
DoConstant10000();
DoField10000();
// 100000 iterations
DoConstant100000();
DoField100000();
}
private static void DoField100000()
{
string s = string.Empty;
for (int i = 0; i < 100000; i++)
s += field;
}
private static void DoConstant100000()
{
string s = string.Empty;
for (int i = 0; i < 100000; i++)
s += Constant;
}
// And so on...
}
Next, I profiled the application with JetBrains DotTrace 3.1 (using the standard configuration options) and got the following results:
We clearly see that for the same iterations, using a string as field rather than a constant has almost a 2:1 performance improvement ratio over the latter. Now, the title of the post is “Why you should use…” and not “Why you must use…” because there are some scenarios when using a string constant is better suited, such as when the constant is being used as a case in a switch statement (you cannot use a static readonly field as a case in a switch statement because its value will not be known until runtime).
Another thing you should be conscious of is relying on your experience rather than a tool’s recommendation. For example, when you declare a static read-only string variable, ReSharper will recommend you to transform it as a constant. You shouldn’t always obey the tool unless you know exactly the tradeoffs and have considered the impact and risks associated with the decision.
Be a good judge over your codebase. Analyze the evidence and study the tradeoffs. There’s nothing like a good profiler to uncover the truths behind your code.
Similar posts you might be interested in reading:
- Avoid calling a virtual or abstract method from a constructor in C#…especially in VB!
- String vs StringBuilder for the .NET Concatenation Performance Championship
- Inserting and Retrieving An Image In SQL Server 2005
- Evolving your design with the Principle of Least Knowledge
- Rely on your experience and knowledge over some tools’ recommendations
- How do you construct your objects?
- Getting Familiar With Your Basic .NET Delegates






Przemyslaw:
I do not know framework internals so well, but does the fact that you actually test both cases using different strings matter? “Constant” and “field” have different length. Doesn’t it impact concatenation performance?
August 18, 2009, 4:57 amCare to test again with the same string value? e.g.
private const string Constant = “string”;
private static readonly string field = “string”;
Greg Young:
Previous poster is correct. The issue is the concat of different length strings.
const and static readonly are synonyms for each other in the CLR.
Cheers,
Greg
August 18, 2009, 11:23 amMike Duray:
I agree with the two previous posters, but seeing this perked my interest since I wasn’t quite sure what was going on at the IL level.
Here’s my test program:
class Program
{
private const string ConstStr = “Same Value”;
private static readonly string ReadOnlyStr = “Same Value”;
static void Main(string[] args)
{
for (int iteration = 1; iteration <= 5; iteration++)
{
int start = Environment.TickCount;
DoReadOnlyField(Convert.ToInt32(Math.Pow(10, iteration)));
Console.WriteLine(”DoReadOnlyField: Iteration {0}: Ticks elapsed: {1}”, iteration, Environment.TickCount – start);
start = Environment.TickCount;
DoConstField(Convert.ToInt32(Math.Pow(10, iteration)));
Console.WriteLine(”DoConstField: Iteration {0}: Ticks elapsed: {1}”, iteration, Environment.TickCount – start);
Console.WriteLine();
}
Console.ReadKey();
}
private static void DoReadOnlyField(int iterations)
{
string s = string.Empty;
for (int i = 0; i < iterations; i++)
{
s += ReadOnlyStr;
}
}
private static void DoConstField(int iterations)
{
string s = string.Empty;
for (int i = 0; i < iterations; i++)
{
s += ConstStr;
}
}
}
The results? Pretty much inconclusive. Not to nitpick but there’s also the possibility that the GC could interfere with the results. But when digging up the IL I did note the following difference: In DoConstField, there is an instruction:
IL_000d: ldstr “Same Value”
This differs from the comparable instruction in DoReadOnlyField:
IL_000d: ldsfld string StaticVsConst.Program::ReadOnlyStr
I don’t know if this is enough to say that a const field could truly be thought of as an equivalent of a c++ #define (where the variable is replaced with the value at compile time) since the .net const field would still need to be available for reflection. Or at least one would think.
September 26, 2009, 1:12 amMomo:
Dude please remove or correct this post as it is the third result in google and although it has a point about the String.Empty your main argument is misleading. For a year or so I was fooled too and I don’t like the fact that i wrote a lot of static readonly strings thinking that I am actually being smart.
March 8, 2010, 2:41 pm