In C#, both const and readonly are used to create unmodifiable values, but they behave differently in terms of initialization, scope, and usage. Here’s a detailed breakdown of their differences:
1. Initialization
Keyword
When Value is Assigned
Example
const
Compile-time constant: Must be initialized at declaration.
const int MaxValue = 100;
readonly
Runtime constant: Can be initialized at declaration or in the constructor.
readonly string LogPath; (assigned in constructor)
2. Scope
Keyword
Class/Instance Member
Static Behavior
const
Implicitly static (belongs to the class). Accessed via ClassName.ConstValue.
Math.PI (static by default)
readonly
Instance-level by default. Requires static keyword to behave as a class-level constant.
static readonly DateTime StartDate = new DateTime(2023, 1, 1);
3. Supported Types
Keyword
Allowed Types
Example
const
Primitive types (int, bool, char, etc.), string, or null references. Cannot be used with most reference types (except string).
const string AppName = "MyApp";
readonly
Any type, including reference types (e.g., objects, collections).
readonly List<int> Numbers = new List<int>();
4. Memory Behavior
Keyword
Storage
Implications
const
Value is embedded directly into the compiled IL code.
No memory allocation at runtime.
readonly
Value is stored in memory as a regular variable.
Memory is allocated at runtime.
5. Versioning & Recompilation
Keyword
Impact of Value Changes
Example Scenario
const
Requires recompilation of all dependent code if the value changes.
Changing const int Max = 100 to 200 forces dependent assemblies to rebuild.
readonly
No recompilation needed. The value is resolved at runtime.
Update a readonly config value in a DLL without rebuilding dependent code.
6. Usage Scenarios
Keyword
When to Use
Example Use Case
const
Values that never change (e.g., mathematical constants, fixed configurations).
const double Pi = 3.14159;
readonly
Values that are determined at runtime but shouldn’t change after object initialization (e.g., dependency-injected settings, calculated values).
readonly ILogger _logger; (assigned via constructor injection)
7. Example Code
const Example
public class Calculator
{
public const double TaxRate = 0.15; // Must be assigned at declaration
public double CalculateTotal(double price)
{
return price * (1 + TaxRate); // Accessed via class name
}
}
readonly Example
public class AppConfig
{
public readonly string Environment;
public static readonly DateTime StartTime = DateTime.Now; // Static readonly
public AppConfig(string env)
{
Environment = env; // Assigned in constructor
}
}
Key Differences Summary
Feature
const
readonly
Initialization
At declaration
Declaration or constructor
Static by Default
Yes
No (unless static is added)
Supported Types
Primitives, string, null
Any type
Memory
Inlined in IL code
Allocated at runtime
Version Safety
Requires recompilation
No recompilation needed
Use Case
Universal constants
Runtime-determined values
When to Choose Which?
Use const for compile-time constants that are universal and never change (e.g., Math.PI).
Use readonly for values determined at runtime or when working with reference types (e.g., configuration settings, injected dependencies).