C# Constants

A C# constant is a named value that cannot be changed after it is declared. Constants make code easier to read because they replace unexplained literal values with meaningful names. They also protect important fixed values, such as tax rates used in a sample, unit conversion factors, labels, and mathematical values, from accidental reassignment.

Overview: How C# Constants Work

In C#, the keyword const declares a compile-time constant. Compile-time means the value must be completely known when the program is built, before the CLR starts running the program. For example, const int MinutesPerHour = 60; is valid because 60 is a literal value. After that declaration, code can read MinutesPerHour, but it cannot assign a different value to it.

Constants are different from ordinary variables. A variable stores a value that may change while the program runs. A constant represents a fixed value baked into the compiled code. When the compiler sees a constant in many expressions, it can substitute the actual value. This is why constants can be used in places that require compile-time information, such as case labels in a switch statement.

Only certain types can be used with const. Common constant types include numeric types such as int, double, and decimal, plus bool, char, string, and enum values. You cannot create a const array, const list, or const object instance because those require runtime object creation. A string is allowed because string literals are handled specially by the compiler and runtime, and strings are immutable.

There is an important companion feature called readonly. A readonly field cannot be changed after construction, but its value does not have to be known at compile time. That makes readonly suitable for values loaded from configuration, calculated in a constructor, or created with new. Use const for true fixed facts known at build time; use readonly when the value is fixed after an object or type is initialized.

Constants also affect API design. A public const value from one assembly can be copied into another assembly at compile time. If the library later changes the constant, existing compiled clients may still use the old value until they are rebuilt. For values exposed across assembly boundaries, public static readonly is often safer unless the value is truly permanent.

Syntax

const type ConstantName = constantValue;
public const type ConstantName = constantValue;
readonly type fieldName = value;
Part Meaning
const Creates a compile-time constant that must be initialized in the declaration and cannot be assigned later.
type The constant’s type, such as int, decimal, bool, char, string, or an enum type.
ConstantName The identifier used to read the constant. Public constants commonly use PascalCase.
constantValue A literal or another compile-time constant expression that the compiler can evaluate during compilation.
readonly Creates a field that can be assigned in its declaration or constructor, then remains fixed for that instance or type.

A local constant inside a method is written like this:

const double InchesPerCentimeter = 0.3937007874;
const string UnitLabel = "in";

Examples

Using Constants for Unit Conversion

using System;

class Program
{
    static void Main()
    {
        const int MinutesPerHour = 60;
        const int HoursPerWorkday = 8;

        int workdays = 5;
        int totalMinutes = workdays * HoursPerWorkday * MinutesPerHour;

        Console.WriteLine($"Workdays: {workdays}");
        Console.WriteLine($"Minutes per hour: {MinutesPerHour}");
        Console.WriteLine($"Total minutes: {totalMinutes}");
    }
}

Output:

Workdays: 5
Minutes per hour: 60
Total minutes: 2400

The values 60 and 8 are not mysterious numbers scattered through the calculation. Their names explain the formula. If the program tried to assign MinutesPerHour = 61;, the compiler would reject it.

Constants in a Price Calculation

using System;
using System.Globalization;

class Program
{
    static void Main()
    {
        const string ProductCodePrefix = "BK";
        const decimal ListPrice = 24.95m;
        const decimal DiscountRate = 0.10m;

        decimal discount = ListPrice * DiscountRate;
        decimal salePrice = ListPrice - discount;

        Console.WriteLine($"Product: {ProductCodePrefix}-1024");
        Console.WriteLine($"List price: {ListPrice.ToString("F2", CultureInfo.InvariantCulture)}");
        Console.WriteLine($"Discount: {discount.ToString("F2", CultureInfo.InvariantCulture)}");
        Console.WriteLine($"Sale price: {salePrice.ToString("F2", CultureInfo.InvariantCulture)}");
    }
}

Output:

Product: BK-1024
List price: 24.95
Discount: 2.50
Sale price: 22.46

This example uses const string and const decimal. Notice the m suffix on decimal literals. Without it, values such as 24.95 are treated as double literals, and the assignment to a decimal constant would not compile.

Constants in switch Labels

using System;

class Program
{
    static void Main()
    {
        const int BronzeLevel = 1;
        const int SilverLevel = 2;
        const int GoldLevel = 3;

        int memberLevel = SilverLevel;
        string benefit;

        switch (memberLevel)
        {
            case BronzeLevel:
                benefit = "Standard newsletter";
                break;
            case SilverLevel:
                benefit = "Newsletter and early access";
                break;
            case GoldLevel:
                benefit = "All benefits plus priority support";
                break;
            default:
                benefit = "No benefits assigned";
                break;
        }

        Console.WriteLine($"Level: {memberLevel}");
        Console.WriteLine($"Benefit: {benefit}");
    }
}

Output:

Level: 2
Benefit: Newsletter and early access

case labels must be constant expressions. The named constants keep the switch readable while still satisfying the compiler’s requirement that each label is known at compile time.

When readonly Is the Better Choice

using System;

class Program
{
    static void Main()
    {
        var settings = new ReportSettings("monthly", 12);

        Console.WriteLine($"Report type: {settings.ReportType}");
        Console.WriteLine($"Months retained: {settings.MonthsRetained}");
    }
}

class ReportSettings
{
    public readonly string ReportType;
    public readonly int MonthsRetained;

    public ReportSettings(string reportType, int monthsRetained)
    {
        ReportType = reportType;
        MonthsRetained = monthsRetained;
    }
}

Output:

Report type: monthly
Months retained: 12

The constructor arguments are not known at compile time, so they cannot initialize const fields. They can initialize readonly fields because readonly allows assignment during construction and then prevents later changes.

How Constants Work Step by Step

  1. The compiler reads a declaration such as const int MaxAttempts = 3;.
  2. It checks that the type is allowed for constants and that the initializer can be evaluated at compile time.
  3. It records the name, type, and value in the program’s metadata and symbol table.
  4. When the constant is used in an expression, the compiler can treat it as the literal value. For example, MaxAttempts + 1 can be evaluated as 3 + 1.
  5. At runtime, there is no ordinary local variable slot whose value can be reassigned. The compiled instructions use the constant value directly where appropriate.

This does not mean constants are always faster in a way you should optimize for manually. The main benefit is correctness and clarity. The compiler and JIT already perform many optimizations; your job is to give stable values clear names and choose the right immutability feature.

Common Mistakes

Trying to Reassign a Constant

const int MaxRetries = 3;
MaxRetries = 4;

This does not compile because a constant cannot appear on the left side of an assignment after it is declared. If the value is meant to change during the program, use a variable.

int maxRetries = 3;
maxRetries = 4;
Console.WriteLine(maxRetries);

Output:

4

Using a Runtime Value as a const Initializer

string folder = Console.ReadLine();
const string DefaultFolder = folder;

This does not compile. Console.ReadLine() runs at runtime, and a local variable value is not a compile-time constant. For a fixed value that comes from runtime input or configuration, use readonly in a class or an ordinary variable in a method.

string folder = "reports";
string defaultFolder = folder;
Console.WriteLine(defaultFolder);

Output:

reports

Forgetting the decimal Suffix

const decimal TaxRate = 0.0825;

This does not compile because 0.0825 is a double literal. Add the m suffix to make the literal a decimal.

const decimal TaxRate = 0.0825m;
Console.WriteLine(TaxRate);

Output:

0.0825

Best Practices

  • Use const for true compile-time facts: conversion factors, fixed labels, mathematical constants, and values that are not expected to change between builds.
  • Use descriptive names such as MinutesPerHour instead of vague names such as Number or Value.
  • Prefer PascalCase for constants that behave like named values, especially fields. Follow your team’s style for local constants.
  • Use decimal constants with an m suffix for money-like values.
  • Use readonly or static readonly when the value is created at runtime, read from configuration, or exposed publicly from a reusable library.
  • Avoid making every repeated literal a constant. Name a value when the name explains meaning, prevents mistakes, or centralizes a real rule.
  • Do not use public const for values that might change in a separate library version unless you are comfortable requiring consumers to rebuild.

Practice Exercises

  1. Create constants for DaysPerWeek and HoursPerDay. Store a number of weeks in a variable and print the total hours.
  2. Write a program with a const decimal TaxRate, a product price, and a quantity. Print the subtotal, tax, and total with two decimal places.
  3. Create three integer constants for shipping speeds, then use a switch statement to print the delivery description for one selected speed.

Summary

  • const declares a value that is known at compile time and cannot be reassigned.
  • Constants improve readability by giving meaningful names to fixed values.
  • Constant initializers must be literals or other compile-time constant expressions.
  • Constants can be used in places that require compile-time values, such as switch case labels.
  • readonly is different: it protects a field after construction but allows runtime initialization.
  • Choose const for permanent facts and readonly for values that are fixed only after initialization.