Iris Classon
Iris Classon - In Love with Code

Stupid Question 46: Static readonly VS Const, which one to use?

[To celebrate my first year of programming I will ask a ‘stupid’ questions daily on my blog for a year, to make sure I learn at least 365 new things during my second year as a developer]

The weather in Gothenburg is pretty constant… rain, rain and more rain

If I have a value that will never change I’ve always used const (and if it is a value type), and I’ve used static readonly with complex types, although not to often. But, I will gladly admit that today I googled this to double check whether I’ve been thinking right or not- as I couldn’t really remember where I first got my habits from :D

MSDN :

A static readonly field is useful when a symbolic name for a constant value is desired, but when the type of the value is not permitted in a const declaration, or when the value cannot be computed at compile-time

public static readonly Color Black = new Color(0, 0, 0);

VS

public const int c1 = 5.0;

MSDN

The readonly keyword is different from the const keyword. A const field can only be initialized at the declaration of the field. A readonly field can be initialized either at the declaration or in a constructor. Therefore, readonly fields can have different values depending on the constructor used. Also, while a const field is a compile-time constant, the readonly field can be used for runtime constants, as in the following example: public static readonly uint l1 = (uint) DateTime.Now.Ticks;

Comments

Leave a comment below, or by email.
TheCodeJunkie
9/18/2012 6:40:24 AM
Const has to be set at the same time you declare it. Readonly if you want to set the value, at runtime, in the constructor. Static just makes it accessible outside the type in a shared variable. 
Michael Lund
9/18/2012 6:41:25 AM
Further: public const in assembly A might be seen from  assembly B and optimized inline in assembly B.
If you then change the value in assembly A, and recompile and distribute that to theapplication using assembly B, then B will still contain the old value. That will not happen with static readonly. 
Fredrik Mörk
9/18/2012 6:45:18 AM
I blogged about the difference a while ago: "static readonly" vs. "const" in c#.

One of the most obvious differences is that you can't use a static readonly in a case statement in a switch block. 
Filip Ekberg
9/18/2012 6:47:52 AM
There's also a difference in how these are stored / loaded.

const int y = 5;

Console.WriteLine(y);

The above will be represented like this with IL:


IL_0001:  ldc.i4.5    
IL_0002:  call        System.Console.WriteLine


Which basically means that it just loads it as a constant value (hence no variable/placeholder used).

Whereas a readonly field like this:

public readonly int X = 10;

Used in a context like this:


public readonly int X = 10;
void Main()
{
	var x = X * 5;
}


Will result in the following IL:

IL_0001:  ldarg.0     
IL_0002:  ldfld       UserQuery.X
IL_0007:  ldc.i4.5    
IL_0008:  mul         
IL_0009:  stloc.0     


As you can see, it differes a bit on IL level as well, which can be important to understand as well. 

Great post by the way! 
Adam Tuliper
9/18/2012 6:51:54 AM
Also of minor importance is the difference between initializing at declaration and in the constructor for a static readonly field.
Note that in the constructor - initialization is done when that class is used for the first time.

public class Foo
{
//As a field initializer ie
private static readonly List _yourList = new List;
}

vs

public class Foo()
{

  private static readonly List _yourList;
  static Foo()
  {
     _yourList = new List;
  }

}


The initialization on the first inline initializer will occur when YourList _yourList is first accessed. In the second case it is initialized when the type Foo is accessed. 
Filip Ekberg
9/18/2012 6:53:07 AM
Just a side note, if you have a constant string and you concatenate it with something, here how const vs non-const differs:

const string y = "Test";
var x = y + "1";

IL:


IL_0001:  ldstr       "Test1"
IL_0006:  stloc.0     


And the non-const version:


string y = "Test";
var x = y + "1";


IL:


IL_0001:  ldstr       "Test"
IL_0006:  stloc.0     
IL_0007:  ldloc.0     
IL_0008:  ldstr       "1"
IL_000D:  call        System.String.Concat
IL_0012:  stloc.1   
 
Steve Crane
9/18/2012 7:57:09 AM
One thing to be aware of is that using readonly or static readonly with complex types doesn't make them immutable. Yes, the variable is immutable so you can't assign a different instance to it, but the instance it refers to is not and you can modify its properties. 
Joep Beusenberg
9/18/2012 11:22:22 AM
As always, there are also semantics involved:
A const is constantly the same thing, even bridging different instances of the application. A true *compile time constant*. Pi equals Pi tomorrow equals Pi yesterday: it'll never change.

A static readonly field is just that: a field that will be set once and remains the same. But different applications have different instances of the value. If decorated with [TreadStatic] it'll even have a new instance assigned per thread.

For the purists: yes this goes for the "const string" too. Even though a string seems like a reference object, the compile time constant is not one in a way. It actually points to a fixed offset in the assembly, not on the heap. At assignment of const string Foo = "Bar"; there will be no new instance of the string created. It already exists at compile time, and will never be garbage collected by the runtime, since the loaded assembly is in a readonly page. 
James Curran
9/18/2012 2:45:39 PM
Reply to: Adam Tuliper
Are you sure about that?  From my experience, stepping through code with the debugger, the compiler appears to just translate the first form into the second. 
James Curran
9/18/2012 2:51:01 PM
Reply to: Steve Crane
To give a concrete example of that:

class Node
{
   /// This is just an example, I wouldn't recommend a public field.
   public readonly List Children = List();
   // etc.
}

You can add and remove child nodes, you just can't replace the whole collection. 
James Curran
9/18/2012 2:55:07 PM
Reply to: Michael Lund
Wow.... I didn't believe that at first, and actually looked it up in the C# specification.   (It's not explicitly stated in the standard, but some clause suggest that it is possible) 
Joep Beusenberg
9/18/2012 3:14:47 PM
That's the difference between a struct and a class, indeed. Where a struct (ValueType) is represented by an array of bytes containing the value, a class (ReferenceType) is represented deep down by just a pointer to a block of memory. That pointer is the one that gets to be readonly. The data it points to remains as it was, and might be mutable in itself.
That's why they had to make string a Pure type. You cannot change a string, because it points to a string of bytes somewhere that could be in protected memory. That, and of course this Pure approach solves all the terrible allocating and copying and terminating and cleaning you have to take care of in unmanaged code. It comes at a price though: every change of a string requires it to be copied. Even when the original string will be overwritten. That's something to keep in mind when you do a Trim or ToUpper on a 2GB string. Use MutableSting then, but don't be fooled: mutablestring.ToSting() still has to make a copy. 

And so we arrive at Streams and undeterminated Enumerables. Totally off-topic now, but maybe a good topic for a future 'stupid question'... 


Last modified on 2012-09-17

comments powered by Disqus