Friday, August 06, 2010

C# .NET const vs readonly

I recently ran into a bug on an old code base which was partly updated.

One of the dependencies / libraries of an application was updated but not the main application. There were no breaking changes in the library which warranted a full deploy.

To illustrate the concept I have this small trivial console application.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Lib;

namespace ConstVsReadOnly
{
 class Program
 {
  static void Main(string[] args)
  {
   Console.WriteLine(Lib.Constants.MY_CONSTANT);
   Console.WriteLine(Lib.Constants.myReadonly);
   Console.ReadLine();
  }
 }
}


And the library

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Lib
{
 public class Constants
 {
  public const string MY_CONSTANT = "const string MY_CONST";
  public static readonly string myReadonly = "static readonly string myReadonly";
 }
}


The above code outputs the following

const string MY_CONST
static readonly string myReadonly

The main application and the libraries were deployed together back in time and now only the library was copied to the deployment location. For illustration let us update the Lib code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Lib
{
 public class Constants
 {
  public const string MY_CONSTANT = "const string MY_CONST some new value";
  public static readonly string myReadonly = "static readonly string myReadonly another new value";
 }
}

If not looking very closely at the code I would expect the code to output
const string MY_CONST some new value
static readonly string myReadonly another new value

however the above code outputs the following

const string MY_CONST
static readonly string myReadonly another new value


This actually make sense but let us dive into the MSIL to look at what is happening.

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       30 (0x1e)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldstr      "const string MY_CONST"
  IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_000b:  nop
  IL_000c:  ldsfld     string [Lib]Lib.Constants::myReadonly
  IL_0011:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0016:  nop
  IL_0017:  call       string [mscorlib]System.Console::ReadLine()
  IL_001c:  pop
  IL_001d:  ret
} // end of method Program::Main

The constant have been changed into the actual value of the constant defined in the library rather than just a reference to the constant/variable.

That in itself is nothing new but it just shows that when deploying solutions with constants that are not so constant between releases then you need to rebuild and update the main application as well  since the referenced constant in the main application is replaced the the constants actual value.

Post a Comment