const and readonly
const answers when a value is known. readonly controls whether a runtime value or access path may be modified.
The distinction
const belongs to compile-time evaluation. readonly belongs to runtime mutability and access control.
A value can be known at compile time without representing a runtime variable. Conversely, a runtime value can be read-only even when it is produced by a function, file, network request or another runtime operation.
const: compile-time values
A const declaration must have an initializer that the compiler can evaluate. Constants may be declared at module level or associated with a struct.
const int MaxPlayers = 64;
const int BufferSize = MaxPlayers * 1024;
struct Physics
{
const float Gravity = 9.81f;
}
Constant expressions may combine literals, other constants, arithmetic, comparisons, bitwise and boolean operations, primitive casts, sizeof, alignof and offsetof.
const int VectorSize = sizeof(Vector3);
const int YOffset = offsetof(Vector3, Y);
readonly: runtime immutability
A readonly value may be calculated at runtime. Once initialized, a readonly local or field cannot be assigned again.
readonly int result = ReadValue();
// result exists at runtime, but cannot be reassigned.
readonly is also used for read-only references, read-only pointer access and instance methods that do not mutate their receiver.
readonly Vector3& position = GetPosition();
void Print(readonly byte* message);
public readonly float Length()
{
return Sqrt(X * X + Y * Y + Z * Z);
}
Readonly access paths
For pointers and references, readonly restricts mutation through that particular access path. It does not necessarily make the underlying memory globally immutable.
Vector3 value = Vector3(1.0f, 2.0f, 3.0f);
Vector3& mutableView = value;
readonly Vector3& readonlyView = value;
mutableView.X = 10.0f; // allowed
// readonlyView.X = 10.0f; not writable through this reference
In the parameter readonly byte* message, the pointed-to bytes cannot be modified through message. The pointer is a runtime value; the declaration is not a compile-time constant.
Choosing the keyword
Use const when the compiler must know and evaluate the value. Use readonly when the value or access path exists at runtime but mutation must be restricted.
const int HeaderSize = 16; // compile time
readonly int bytesRead = Read(); // runtime