Skip to content
Author: ytianle

const vs static readonly⚓︎

const⚓︎

In C#, const declares a compile-time constant whose value is baked into callers. A const must be initialized at its declaration with a constant expression.

Allowed const types are numeric types, char, bool, string, enum, and null (for reference types).

Here are some examples about what can be assigned to a const:

  • literals (10, "Hello", true)
  • other const values
  • simple compile-time expressions (1 + 2, 3 * 4)
  • compile-time constants from the language (Math.PI, DayOfWeek.Monday)
public class Example
{
    public const int MyConst = 10;  // assigned at declaration, cannot be changed after compilation
    public void PrintConst() => Console.WriteLine(MyConst);
}

static⚓︎

In C#, static is a keyword used to declare a member that belongs to the type itself rather than to any specific instance of the type. A static member can be accessed without creating an instance of the class.

public class Example
{
    public static int MyStaticValue = 20; // belongs to the class, can be accessed without an instance
}
public class Program
{
    public static void Main()
    {
        Console.WriteLine(Example.MyStaticValue); // Output: 20
    }
}

readonly⚓︎

In C#, readonly is a keyword used to declare a field that can only be assigned a value during its declaration or within a constructor of the same class. A readonly field cannot be modified after the constructor has finished executing.

public class Example
{
    public readonly int MyReadonlyValue = 30; // can be assigned at declaration

    public Example(int value)
    {
        MyReadonlyValue = value; // can be assigned in constructor
    }
}

const vs static readonly⚓︎

One-line conclusion⚓︎

const = compile-time constant (inlined into callers).

static readonly = runtime readonly field (value read from the declaring assembly at runtime).

Quick comparison⚓︎

Dimension const static readonly
Evaluation time Compile-time (inlined) Runtime (field lookup)
Assignment At declaration only Declaration or static constructor
Types supported Numeric, char, bool, string, enum, null Any type
Inlining Yes (caller gets baked value) No
Cross-assembly safety Unsafe — callers must recompile after change Safe — update assembly without recompiling callers
Can come from config/env No Yes
Usable in attributes / switch case Yes No
Cross-assembly safety

When a const value is changed in a library, all callers that reference it must be recompiled to see the new value. If they are not recompiled, they will continue to use the old value, which can lead to subtle bugs. In contrast, static readonly fields are accessed at runtime, so changes to their values in the library will be reflected in all callers without needing recompilation.

Minimal examples⚓︎

// in library
public static class C {
    public const int Timeout = 30;           // inlined into callers
    public static readonly TimeSpan TS = TimeSpan.FromSeconds(30); // runtime field
}

// in caller
Thread.Sleep(C.Timeout * 1000); // compiled as Thread.Sleep(30 * 1000)
// If library changes Timeout to 60, caller still uses 30 until recompiled.

Practical recommendations⚓︎

  • public API (public types, methods, properties, fields, events exposed by a library/assembly and callable by external code): prefer static readonly, which is generally safer and more flexible, as it allows for changes without breaking binary compatibility.
  • Internal/private/local: for private or local constants that are truly constant and won't change, const is acceptable and can be more efficient.
  • Use const when required: attribute parameters, switch case labels, or compile-time language constants (e.g., mathematical constants).