Constant declarations vs Hard-coded literals in C#

Diego Martin | 05 may. 2020

Logo Constant

One of the most recurring debates I've had in every development team I've been part of is whether to favor constant declaration over hard-coded literals or not.

As always, there is not a straight answer because it depends on several factors such as:

  1. Is the value used more than once in your code?
  2. Is the value meaningful outside the context is being used?
  3. What kind of type are we talking about?
  4. What makes the code more readable?

But first let's start by analyzing what constants and literals really are. Literals are constants, in fact they are synonyms. They are simply values that cannot change during code execution, so what we're discussing here is whether we should declare constants like const <data_type> <constant_name> = value; as in:

private const string Greeting = "Welcome to Sunny Attic Software";

or simply not to declare them beforehand and use the literal when needed (i.e: the hard-coded way) as in:

SayGreeting("Welcome to Sunny Attic Software");

Intermediate Language when using constants

Now let's have a look at how a C# code compiles to IL (Common Intermediate Language) when using the first approach compared to the second. To do the comparison we can use ReSharper, PEVerify.exe or any other tool available to browse compiled code in .Net

The following class that uses constant declaration.

using System;

namespace ConstantDeclaration
{
    class Program
    {
        static void Main(string[] args)
        {
            const string value = "Hello World!";
            Console.WriteLine(value);
        }
    }
}

It generates the following IL.

.class private auto ansi beforefieldinit 
  ConstantDeclaration.Program
    extends [System.Runtime]System.Object
{

  .method private hidebysig static void 
    Main(
      string[] args
    ) cil managed 
  {
    .entrypoint
    .maxstack 8

    // [8 9 - 8 10]
    IL_0000: nop          

    // [10 13 - 10 38]
    IL_0001: ldstr        "Hello World!"
    IL_0006: call         void [System.Console]System.Console::WriteLine(string)
    IL_000b: nop          

    // [11 9 - 11 10]
    IL_000c: ret          

  } // end of method Program::Main

  .method public hidebysig specialname rtspecialname instance void 
    .ctor() cil managed 
  {
    .maxstack 8

    IL_0000: ldarg.0      // this
    IL_0001: call         instance void [System.Runtime]System.Object::.ctor()
    IL_0006: nop          
    IL_0007: ret          

  } // end of method Program::.ctor
} // end of class ConstantDeclaration.Program

Intermediate Language when using literals

Now, the following class hard-codes literals instead.

using System;

namespace HardcodeLiterals
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}

and it generates the following IL.

.class private auto ansi beforefieldinit 
  HardcodeLiterals.Program
    extends [System.Runtime]System.Object
{

  .method private hidebysig static void 
    Main(
      string[] args
    ) cil managed 
  {
    .entrypoint
    .maxstack 8

    // [8 9 - 8 10]
    IL_0000: nop          

    // [9 13 - 9 47]
    IL_0001: ldstr        "Hello World!"
    IL_0006: call         void [System.Console]System.Console::WriteLine(string)
    IL_000b: nop          

    // [10 9 - 10 10]
    IL_000c: ret          

  } // end of method Program::Main

  .method public hidebysig specialname rtspecialname instance void 
    .ctor() cil managed 
  {
    .maxstack 8

    IL_0000: ldarg.0      // this
    IL_0001: call         instance void [System.Runtime]System.Object::.ctor()
    IL_0006: nop          
    IL_0007: ret          

  } // end of method Program::.ctor
} // end of class HardcodeLiterals.Program

As you can see, the compiled code is identical regarding the instruction to print on console the greeting. In both cases the compiler does exactly the same with the code, it declares a constant internally and later it uses it, so there are no benefits of one way over the other in terms of performance. That leaves the debate to be decided upon other factors such as reducing error prone code and improve readability. After all, let's not forget we write code primarily for other developers and ourselves to read it later on.

What's best for reducing error prone code?

Let's be clear here. If the same value is used more than once in our code, this value should be extracted into a constant declaration. There is no question about that benefit of DRY principle because:

  • If you refactor your code and change the value you don't need to go one by one finding all the hard-coded literals changing them.
  • It avoids making a mistake (e.g: a typo) in any of the values, you just have to focus in not making mistakes when declaring the only constant you will be using.

If the same value is used across different classes and files, you could place the constant declaration in a class that has the only responsibility of keeping all the constants for a specific context within your application.

If the value is used in different methods within the same class, you could declare the constant as a private member of the class (pascal case naming convention), and if the value is used multiple times within the same method you could declare the constant inside this method as a local constant (camel case naming convention). Always declare the constant inside the context where is going to be used, where it is more meaningful.

What's best for improving readability?

Here's where there is an open debate. Again, there's no question that if a value is used multiple times, there should be a single constant declaration to reuse this value. But what if the value is used once and only once?

My answer here is: it depends on each case.

Let's imagine the value is an Integer literal such as the number of working days per week for a worker, which is 5. If we use the hard-coded literal in the following way:

GeneratePayslip(userId, 5);

then somebody who is reading our code may wonder what's the magic number about.

However if we had used the value in the following way:

const int daysWorkedPerWeek = 5;
GeneratePayslip(userId, daysWorkedPerWeek);

any potential reader would immediately know what that value means and understands the code better.

But let's imagine now that our value is a string such as a greeting sentence that we want to use only once in a specific context. If we do something like this:

SayGreeting($"Welcome again to this section of Sunny Attic Software" {name}");

Wouldn't that make more sense than extracting the greeting into a constant that, honestly, we wouldn't be able to find a good meaningful name for? Like this:

const string greetingToDiegoDrivenDesignSection = "Welcome again to this section of Sunny Attic Software";
SayGreeting($"{greetingToDiegoDrivenDesignSection} {name}");

TL;DR

Summarizing at the Too Long Didn't Read section, my conclusion is that you should always wonder what's more meaningful for somebody who is going to read our code.

If declaring a constant can help to better understand the magic literal, go ahead. But if the name you'd give to a constant is something that does not make things more readable, just use the hard-coded literal because that's probably way more meaningful. Of course, as long as this literal is used just once in a specific context.