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
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, π(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.
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 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 and then pop it into the local variable 0 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#.
- 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.
ldstr <string> - Push a string object for the literal string. ↩︎
stloc <uint16 (index)> - Pop a value from stack into local variable indx. ↩︎