Back to Blog
C#

C# const keyword usage: Compile-Time Constants

c# const keyword usage: Learn how the const keyword works in C#, its compile-time nature, differences from readonly, and when to use it effectively.

constcompile-time constantsreadonlyC# languageperformance
A visual representation of a C# const keyword being substituted at compile time, showing a literal value embedded in code.

The const keyword in C# declares a compile-time constant. When you use a const value, the compiler replaces every reference with the literal value at compile time. This is the fundamental behavior that shapes how and when you should use const in your code. Understanding c# const keyword usage starts with recognizing that const is not a runtime variable—it is a compile-time substitution.

What const Means in C#

A const member is a field or local variable whose value is fixed at compile time. The compiler must be able to evaluate the value at compile time, which means the initializer must be a constant expression. This includes numeric literals, string literals, character literals, boolean literals, and expressions composed of other const values. You cannot use a const to store the result of a method call, a new expression, or any value that requires runtime evaluation.

public const int MaxRetries = 3; public const string AppName = "OrderService"; public const bool EnableLogging = true;

These declarations are valid because the right-hand side is a compile-time constant. The compiler copies these values into the metadata and substitutes them wherever the const is referenced.

Declaring const Variables

You can declare const at the class level as a field, or locally inside a method. The syntax is straightforward, but there are important restrictions on the type. A const field must be a primitive type, string, or an enum type. Reference types other than string are not allowed because their values are created at runtime.

public class Settings { public const int DefaultPort = 8080; public const string ServiceName = "Billing"; } public void Process() { const double ConversionFactor = 2.54; // local const usage }

Local const variables follow the same rules. They are useful for named numeric or string literals that appear multiple times within a method, making the code more readable without introducing a runtime field.

const vs readonly: Choosing the Right Constant Type

Developers often confuse const with readonly. Both create immutable values, but they behave differently. const is evaluated at compile time, while readonly is evaluated at runtime. This distinction has significant consequences for type support, versioning, and memory usage.

Aspectconstreadonly
Evaluation timeCompile timeRuntime
Allowed typesPrimitives, string, enumAny type (including reference types)
Instance vs staticAlways staticCan be instance or static
Memory allocationNo runtime allocation; value embedded in ILAllocated per instance or static
VersioningChanging value requires recompiling all consumersChanging value only requires recompiling the defining assembly

Use readonly when the value cannot be determined at compile time, such as a DateTime instance, a Guid, or a custom class. Use const when you have a true compile-time constant and you accept the versioning implications.

Where const Can Be Used

const is most appropriate for values that are truly invariant and unlikely to change across versions. Common examples include mathematical constants, configuration keys, and protocol constants. Because the compiler inlines the value, using const for a public API can be risky. If you change the value of a public const field, every assembly that references it must be recompiled; otherwise, the old value remains in the compiled consumer code.

public class ApiConstants { public const int MaxPageSize = 100; public const string HeaderName = "X-Request-Id"; }

In this example, MaxPageSize and HeaderName are used across the codebase. If you later change MaxPageSize to 200, you must rebuild all dependent projects to pick up the new value. For internal code, this is usually acceptable. For public libraries, consider readonly or a static property to avoid forcing recompilation on consumers.

Limitations and Common Mistakes

The most common mistake is trying to use const with a type that requires runtime initialization. For example, you cannot declare public const DateTime DefaultDate = new DateTime(2024, 1, 1); because new DateTime(...) is not a constant expression. Similarly, const cannot hold an array or a list, even if the elements are constants. The compiler simply does not support reference types other than string.

Another mistake is assuming const is the same as static readonly. While both are static by default, readonly can be assigned in a constructor, allowing different values per instance (if not static). const is always static and cannot be assigned at runtime.

public class Example { // Invalid: DateTime is not a primitive or string // public const DateTime Created = DateTime.UtcNow; // Valid: readonly can hold runtime values public readonly DateTime Created = DateTime.UtcNow; }

When you need a value that is computed once at runtime and then remains fixed, use readonly. When you need a value that is a compile-time literal, use const.

Performance and Maintainability Considerations

Because const values are inlined at compile time, there is no runtime lookup or memory allocation for the constant itself. This can be marginally faster than a readonly field, but the difference is negligible in most applications. The real performance consideration is the opposite: inlining can cause subtle bugs when a constant is changed without recompiling all consumers.

From a maintainability perspective, const is safer when the value is internal to a single assembly or when the value is part of a protocol that must remain stable. For public APIs, prefer readonly or a static property with a getter to allow future changes without breaking binary compatibility.

Another maintainability concern is that const values are embedded in the IL of the consuming code. If you use a const from another assembly, the value is copied into your assembly. This means that if the source assembly changes the value, your assembly still uses the old value until you recompile. This is a classic source of confusion in large solutions.

Best Practices for const Usage

Use const for values that are truly invariant and where you are willing to accept the recompilation requirement. Good candidates include:

  • Mathematical constants like Math.PI (though Math.PI is already a const in .NET)
  • String literals used as keys or names
  • Integer limits that are part of a protocol

Avoid const for values that might change during development, such as connection strings, timeouts, or feature flags. For those, use readonly or configuration. Also avoid const for public API fields unless you are certain the value will never change. If there is any chance of change, use readonly to avoid breaking consumers.

When declaring a local constant, prefer const over a var with a literal when the value is used multiple times and has a clear meaning. This improves readability without runtime cost.

public void SendRequest() { const int TimeoutSeconds = 30; var client = new HttpClient { Timeout = TimeSpan.FromSeconds(TimeoutSeconds) }; // ... }

In this example, TimeoutSeconds is a named literal that clarifies the intent. If you later need to change it, you only edit one line.

When const Causes Subtle Runtime Behavior

One less obvious behavior is that const values are evaluated at compile time, so any expression that uses them is also evaluated at compile time. This can lead to integer overflow checks being performed at compile time, not runtime. For example, const int Max = int.MaxValue; const int Next = Max + 1; produces a compile-time error because the addition overflows in a checked context. This is actually a benefit: it catches invalid constant arithmetic early.

However, this also means that const cannot be used with volatile or with any attribute that requires runtime state. The compiler treats const as a literal, so it is not a storage location. This is why you cannot pass a const by reference or use it as an out parameter. If you need a variable that can be passed by reference, use readonly instead.

Understanding these boundaries helps you avoid compile errors and design APIs that are both safe and maintainable. The key is to remember that const is a compile-time feature, not a runtime one.

c# const keyword usage: Practical Usage and Code Examples | RYUSLOG DEV