/ Express yourself

Express yourself - Constants

This is the second instalment in this series exploring the building blocks of computer programming from a C# point of view. For the first instalment, please read Variables and Values

Constant

A simplistic view of a constant in both mathematics and programming is a variable with a value that is fixed and does not change. "A mathematical constant is any well-defined real number which is significantly interesting in some way[1], π(Pi) is one such example of a "significantly interesting" number.

Rather than just numbers, in the context of programming, a constant is a variable that can not be changed in the course of executing a program. In C# The initial value of a constant must be able to be evaluated at compile time[2].

In the below example, we set the 'a' to constantly be Bens.

const string a = "Bens";

You are also able to use constants to define other constants.

const string a = "Bens";
const string b = "Cafe " + a;

There is one caveat though, consider this code:

const int a = 5;
const int b = Math.Sin(a);

Here we will receive an error as the line const int b = Math.Sin(a); can not be evaluated untill the program is executed.

After Joe had so much success with his webpage menu it's hard for him to remember when the Cafe first opened and how the menu was updated then. Before the blackboards, there were two large menus that were made of wood that Joe had engraved and painted. To change anything on the menu Joe needed to head across to Bunnings[3] to pick up new wood and paint, get out his woodworking hat and create new menus from scratch.

In this example, the wooden menus can be thought of as constants. If any one of the items on the menu needs to change both boards need to be recreated (ignoring the fact that you could stick a piece of paper over the top). Bringing this back to C#, when you change a constant, you also need to recompile your assembly and any other assemblies that reference it, and that does not sound like a very SOLID thing to do.

Let's have a look at some code the could represent Joe's wooden menu boars.

const string cafeName = "Bens Cafe";
const string special = "Today's special is Tuna";

//First Board
Console.WriteLine(cafeName);
Console.WriteLine(special);

//Second Board
Console.WriteLine(cafeName);
Console.WriteLine(special);

Breaking out the MSIL code you can see that the strings are baked in, this is why there is no way of changing the value of a Constant at runtime.

IL_0000:  nop         
IL_0001:  ldstr       "Bens Cafe"
IL_0006:  call        System.Console.WriteLine
IL_000B:  nop         
IL_000C:  ldstr       "Today's special is Tuna"
IL_0011:  call        System.Console.WriteLine
IL_0016:  nop         
IL_0017:  ldstr       "Bens Cafe"
IL_001C:  call        System.Console.WriteLine
IL_0021:  nop         
IL_0022:  ldstr       "Today's special is Tuna"
IL_0027:  call        System.Console.WriteLine
IL_002C:  nop         
IL_002D:  ret       

Now let's look at the same code but this time with value types.

string cafeName = "Bens Cafe";
string special = "Today's special is Tuna";

//First Board
Console.WriteLine(cafeName);
Console.WriteLine(special);

//Second Board
Console.WriteLine(cafeName);
Console.WriteLine(special);

The MSIL once again

IL_0000:  nop         
IL_0001:  ldstr       "Bens Cafe"
IL_0006:  stloc.0     // cafeName
IL_0007:  ldstr       "Today's special is Tuna"
IL_000C:  stloc.1     // special
IL_000D:  ldloc.0     // cafeName
IL_000E:  call        System.Console.WriteLine
IL_0013:  nop         
IL_0014:  ldloc.1     // special
IL_0015:  call        System.Console.WriteLine
IL_001A:  nop         
IL_001B:  ldloc.0     // cafeName
IL_001C:  call        System.Console.WriteLine
IL_0021:  nop         
IL_0022:  ldloc.1     // special
IL_0023:  call        System.Console.WriteLine
IL_0028:  nop         
IL_0029:  ret  

The output of the above samples is exactly the same, but there are differences in the IL. This time on the lines IL_0001, IL_0006 we push the string "Bens Cafe" onto the stack[4] and then pop it into the local variable 0[5] with the instructions

ldstr       "Bens Cafe"
stloc.0     // cafeName

This is repeated on the lines IL_0007, IL_000C but pops it in local variable 1 this time.

With less going on under the hood when using constants it is easy to conclude that there is a performance benefit, and there may well be in large applications. In my samples, the execution time was exactly the same two milliseconds. I would argue that any performance benefit you may experience by exclusively using constants would be outweighed (by far) with the overhead of having to rebuild any assemblies that use a constant that changes, on top of the inability to assign the value of a constant with an expression. As a rule of thumb, I would only use constants for representing things that are constant (pun intended) like the speed of light or Pi, relying on read-only variables for everything else but that is a story for another time.

Please feel free to ask anything, point out where I am wrong, or suggest improvements. My main aim is to consolidate my understanding of programming from a lens of C#.

Edits:

  • Expanded on the differences between constants and variables by cracking open MSIL and explored why you might not want to use them in the majority of cases. Shout out to Viking for the suggestion that I explain when I might use them or not.

  1. http://mathworld.wolfram.com/Constant.html ↩︎

  2. Constants in C# are far more complicated than what can be explained in the scope of this post. Please see the Language Specification for more information. ↩︎

  3. For thoes not in Australia, Bunnings is a hardware store that has sausage sizzles. ↩︎

  4. ldstr <string> - Push a string object for the literal string. ↩︎

  5. stloc <uint16 (index)> - Pop a value from stack into local variable indx. ↩︎