Beware of const members

I happened to learn a new thing about const today and how one needs to be careful with its usage.

Let’s say I have a third-party assembly ‘ConstVsReadonlyLib’ with a class named ConstSideEffect.cs:

   1: public class ConstSideEffect
   2: {
   3:     public static readonly int StartValue = 10;
   4:     public const int EndValue = 20;
   5: }

In my project, I reference the above assembly as follows:

   1: static void Main(string[] args)
   2: {
   3:     for (int i = ConstSideEffect.StartValue; i < ConstSideEffect.EndValue; i++)
   4:     {
   5:         Console.WriteLine(i);
   6:     }
   7:     Console.ReadLine();
   8: }

You’ll see values 10 through 19 as expected. Now, let’s say I receive a new version of the ConstVsReadonlyLib.

   1: public class ConstSideEffect
   2: {
   3:     public static readonly int StartValue = 5;
   4:     public const int EndValue = 30;
   5: }

If I just drop this new assembly in the bin folder and run the application, without rebuilding my console application, my thinking was that the output would be from 5 to 29. Of course I was wrong… if not you’d not be reading this blog.

The actual output is from 5 through 19. The reason is due to the behavior of const and readonly members.

To begin with, const is the compile-time constant and readonly is a runtime constant. Next, when you compile the code, a compile-time constant member is replaced with the value of the constant in the code. But, the IL generated when you reference a read-only constant, references the readonly variable, not its value.

So, the IL version of the Main method, after compilation actually looks something like:

   1: static void Main(string[] args)
   2: {
   3:     for (int i = ConstSideEffect.StartValue; i < 20; i++)
   4:     {
   5:         Console.WriteLine(i);
   6:     }
   7:     Console.ReadLine();
   8: }

I’m no expert with this IL thingi, but when I look at the disassembled code of the exe file (using IL Disassembler), I see the following:

screen

I see our readonly member still being referenced by the variable name (ConstVsReadonlyLib.ConstSideEffect::StartValue) in line 0001. Then there’s the Console.WriteLine in line 000b and finally, see the value of 20 in line 0017. This, I’m pretty sure is our const member being replaced by its value which marks the upper bound of the ‘for’ loop. Now you know why the output was from 5 through 19.

This definitely is a side-effect of having const members and one needs to be aware of it.

While we’re here, I’d like to add a few other points about const and readonly members:

  • const is slightly faster, but is less flexible
  • readonly cannot be declared within a method scope
  • const can be used only on primitive types (numbers and strings)

Just wanted to share this before going to bed!

6 Comments

  • While your point is useful to remember when creating libraries with a public API, this is not actually a side effect but rather the pure definition of a constant. What makes a const different from a read-only &nbsp;variable is that a const is simply a named value and not actually allocated on the stack. Just wanted to point out the misnomer; still an important point to keep in mind.

  • Good to know! Thank you for sharing this

  • Yeah, that has happened to me more than once. :-( Maybe, one should just keep that "subtle" static/Shared vs. readonly difference in mind. :-)

  • @mtazva.. I agree. I used 'side-effect' a little too loosely.

    @Carsten and @Malachi.. Thanks.

  • A constant should not change, ever. If your 'constant' is likely to change.. it is actually not a constant. PI for example is a real constant.

  • There are other problems with replacing your library assembly without recompiling everything that references it. For example:

    http://blogs.msdn.com/ericlippert/archive/2010/03/29/putting-a-base-in-the-middle.aspx

    The recommended approach is to always recompile your code when any referenced assemblies are changed. The question of whether it's practical to do so, particularly with framework updates, is a more interesting discussion!

Comments have been disabled for this content.