Describe the difference between readonly and const in C#.
6 minbeginner.NETconstantsC#
Quick Answer
const is a compile-time constant that must be assigned at declaration and is embedded in IL code. readonly is a runtime constant that can be assigned in declaration or constructor and is stored in memory. const is implicitly static, while readonly can be instance or static. Use const for true constants, readonly for runtime values.
Detailed Answer
const:
- Compile-time constant
- Value must be assigned at declaration
- Value is embedded in IL code
- Implicitly static
- Can only be primitive types, enums, or string
- Better performance (no runtime lookup)
readonly:
- Runtime constant
- Can be assigned in declaration or constructor
- Value stored in memory
- Can be instance or static
- Can be any type
- More flexible
Examples:
public class Configuration
{
// CONST - Compile-time constant
public const int MaxConnections = 100;
public const string AppName = "MyApp";
public const double Pi = 3.14159;
// READONLY - Runtime constant
public readonly string ConnectionString;
public readonly DateTime StartTime;
public static readonly int ProcessorCount;
// Readonly can be assigned in constructor
public Configuration(string connStr)
{
ConnectionString = connStr;
StartTime = DateTime.Now; // Runtime value!
}
// Static readonly can be assigned in static constructor
static Configuration()
{
ProcessorCount = Environment.ProcessorCount;
}
}
// Usage
// Const access (no instance needed)
int max = Configuration.MaxConnections;
// Readonly access (needs instance or static)
Configuration config = new Configuration("Server=localhost");
string connStr = config.ConnectionString;
Key Differences:
| Feature | const | readonly |
|---|---|---|
| Assignment Time | Compile-time | Runtime |
| Assignment | Only at declaration | Declaration or constructor |
| Modifier | Implicitly static | Can be instance or static |
| Types Allowed | Primitives, string, enum | Any type |
| Performance | Faster (inline) | Slightly slower |
| Value Change | Never | Once per instance/lifetime |
| Memory | IL code | Heap/Stack |
Versioning Issue with const:
// Assembly A (Library)
public class Constants
{
public const int MaxValue = 100;
}
// Assembly B (Application) - uses Assembly A
public class Program
{
public void Process()
{
// Value 100 is embedded in Assembly B's IL code!
int max = Constants.MaxValue;
}
}
// Problem: If you change MaxValue to 200 in Assembly A and recompile only A,
// Assembly B still uses 100 (old value) until B is also recompiled!
Solution with readonly:
// Assembly A (Library)
public class Constants
{
public static readonly int MaxValue = 100;
}
// Assembly B (Application)
public class Program
{
public void Process()
{
// Value is looked up from Assembly A at runtime
int max = Constants.MaxValue;
}
}
// If you change MaxValue to 200 and recompile only A,
// Assembly B automatically gets the new value without recompilation!
readonly with reference types:
public class Container
{
// readonly reference - cannot reassign, but can modify contents
private readonly List<string> items = new List<string>();
public void AddItem(string item)
{
items.Add(item); // OK - modifying contents
// ERROR - cannot reassign
// items = new List<string>();
}
}
readonly struct (C# 7.2+):
// All fields must be readonly
public readonly struct Point
{
public readonly int X;
public readonly int Y;
public Point(int x, int y)
{
X = x;
Y = y;
}
// All properties must be read-only
public int Sum => X + Y;
}
When to use:
- Use const for: True constants that never change (Pi, MaxInt), configuration values that are known at compile-time
- Use readonly for: Values initialized at runtime, dependency injection, values from configuration files, reference types, values that may change between versions
Best Practices:
public class Settings
{
// Good - compile-time constant
public const int DefaultTimeout = 30;
// Good - runtime value from constructor
public readonly string Environment;
// Good - complex object
public readonly ILogger Logger;
// Bad - should be const (never changes)
public readonly int MaxRetries = 3;
public Settings(string environment, ILogger logger)
{
Environment = environment;
Logger = logger;
}
}