C# Variables and Modifiers – Difference Between const, readonly, and static readonly
In C#, const, readonly, and static readonly are used to define values that cannot be reassigned after a certain point.
However, they differ in when values are assigned, whether they belong to an instance or a class, and how they are accessed.
1. What is const in C#?
The const keyword declares a compile-time constant, meaning its value must be known during compilation and cannot be changed.
A const field is implicitly static, so it belongs to the class rather than an individual object.
Example
public class Calculator
{
public const double PI = 3.14159;
}
Console.WriteLine(Calculator.PI);
Explanation:
-
PIis initialized with3.14159. -
Its value is fixed at compile time.
-
It cannot be modified after declaration.
-
It is accessed using the class name.
-
Creating an object is not required.
Can we change a const value?
public class Calculator
{
public const int MaxLimit = 100;
public void Test()
{
// MaxLimit = 200; // Compile-time error
}
}
Why?
Because const values cannot be reassigned after declaration.
Important: const is implicitly static
public class Settings
{
public const int MaxUsers = 100;
}
Correct:
Console.WriteLine(Settings.MaxUsers);
Incorrect:
public static const int MaxUsers = 100;
C# does not allow combining static and const because constants are already implicitly static.
Key Points
-
Compile-Time Constant: Value must be known during compilation.
-
Initialization: Must be assigned at declaration.
-
Modification: Cannot be reassigned.
-
Static: Implicitly static.
-
Access: Through the class name for class constants.
-
Supported Types: Built-in numeric types,
bool,char,string, enums, andnullreferences of reference types. -
Common Usage: Mathematical constants, fixed limits, and constant identifiers.
2. What is readonly in C#?
The readonly keyword declares a field that can be assigned at declaration or inside the constructor of its containing class.
Unlike const, the value can be determined at runtime.
By default, a readonly field belongs to an individual object.
Example
public class Employee
{
public readonly int EmployeeId;
public Employee(int id)
{
EmployeeId = id;
}
}
Usage:
Employee emp1 = new Employee(101);
Employee emp2 = new Employee(102);
Console.WriteLine(emp1.EmployeeId); // 101
Console.WriteLine(emp2.EmployeeId); // 102
Explanation:
-
EmployeeIdis declared asreadonly. -
Its value is assigned inside the constructor.
-
Each object can have a different
EmployeeId. -
After construction, the field cannot be reassigned.
-
readonlyfields are instance-level unless explicitly declaredstatic.
Memory representation
Employee emp1 = new Employee(101);
Employee emp2 = new Employee(102);
Employee Class
|
+------+------+
| |
v v
Employee emp1 Employee emp2
+-----------+ +-----------+
| Id = 101 | | Id = 102 |
+-----------+ +-----------+
Each object has its own readonly field value.
emp1.EmployeeId = 200; // Compile-time error
Can we initialize readonly using a method?
Yes.
public class Employee
{
public readonly DateTime CreatedDate;
public Employee()
{
CreatedDate = DateTime.Now;
}
}
Here, DateTime.Now is evaluated at runtime, which is allowed for readonly fields.
Key Points
-
Runtime Initialization: Values can be determined at runtime.
-
Initialization: At declaration or inside an instance constructor.
-
Modification: Cannot be reassigned after construction.
-
Instance-Level: Each object can have a different value.
-
Access: Usually through an object.
-
Supported Types: Any field type, including custom classes.
-
Common Usage: Employee IDs, configuration values per object, and initialization timestamps.
3. What is static readonly in C#?
The static readonly combination declares a field that belongs to the class and can be initialized at runtime.
Unlike ordinary readonly, the field is shared across all instances of the class.
Example
public class ApplicationSettings
{
public static readonly DateTime StartTime =
DateTime.Now;
}
Usage:
Console.WriteLine(ApplicationSettings.StartTime);
Explanation:
-
StartTimebelongs to theApplicationSettingsclass. -
It is initialized when the type is initialized.
-
The value is shared by all objects.
-
It cannot be reassigned after initialization.
-
No object creation is required to access it.
Can we initialize static readonly inside a constructor?
Yes, but only inside a static constructor, not an instance constructor.
public class ApplicationSettings
{
public static readonly string ApplicationName;
static ApplicationSettings()
{
ApplicationName = "JNTech";
}
}
Usage:
Console.WriteLine(ApplicationSettings.ApplicationName);
Memory representation
ApplicationSettings Class
|
v
+----------------------+
| static readonly |
| AppName = "JNTech" |
+----------------------+
^
|
Shared class-level field
|
+-------+-------+
| |
Object 1 Object 2
All instances share the same static field.
The field cannot be reassigned after
static initialization.
Key Points
-
Class-Level Field: Belongs to the class, not individual objects.
-
Runtime Initialization: Can use runtime values.
-
Initialization: At declaration or in a static constructor.
-
Modification: Cannot be reassigned after initialization.
-
Shared Value: All instances access the same field.
-
Access: Through the class name.
-
Supported Types: Any field type.
-
Common Usage: Shared configuration, application startup values, and immutable shared references.
4. Difference Between const, readonly, and static readonly
|
Feature |
|
|
|
|---|---|---|---|
|
Value determined |
Compile time |
Runtime allowed |
Runtime allowed |
|
Initialization |
Declaration only |
Declaration or instance constructor |
Declaration or static constructor |
|
Default scope |
Class-level |
Instance-level |
Class-level |
|
Implicitly static |
Yes |
No |
Explicitly static |
|
Value shared across objects |
Yes |
No |
Yes |
|
Different values per object |
No |
Yes |
No |
|
Can use |
No |
Yes |
Yes |
|
Can use |
No, except permitted null constants |
Yes |
Yes |
|
Reassignment after initialization |
No |
No |
No |
|
Access |
Class name |
Object |
Class name |
5. Complete Example – All Three Together
public class Employee
{
// Compile-time constant
public const string CompanyName = "JNTech";
// Instance-level readonly
public readonly int EmployeeId;
// Class-level readonly
public static readonly DateTime ApplicationStartTime =
DateTime.Now;
public Employee(int id)
{
EmployeeId = id;
}
}
Usage:
Employee emp1 = new Employee(101);
Employee emp2 = new Employee(102);
// const
Console.WriteLine(Employee.CompanyName);
// readonly
Console.WriteLine(emp1.EmployeeId); // 101
Console.WriteLine(emp2.EmployeeId); // 102
// static readonly
Console.WriteLine(Employee.ApplicationStartTime);
How they work
Employee Class
|
+------------+-------------+
| |
v v
const static readonly
CompanyName="JNTech" ApplicationStartTime
Compile-time value Runtime initialized
| |
+------------+-------------+
|
Shared class data
|
+---------+---------+
| |
v v
Employee emp1 Employee emp2
+-------------+ +-------------+
| readonly | | readonly |
| Id = 101 | | Id = 102 |
+-------------+ +-------------+
const -> Fixed compile-time constant
readonly -> Different value per object
static readonly -> One shared runtime-initialized field
6. Important Interview Scenarios
Scenario 1: Which should we use for a mathematical constant?
Use const.
public const double PI = 3.141592653589793;
Reason: The value is known at compile time and never changes.
Scenario 2: Which should we use for an Employee ID?
Use readonly.
public readonly int EmployeeId;
public Employee(int id)
{
EmployeeId = id;
}
Reason: Every employee can have a different ID, but it should not change after object initialization.
Scenario 3: Which should we use for application startup time?
Use static readonly.
public static readonly DateTime StartupTime =
DateTime.Now;
Reason: The value is determined at runtime and shared across the application through the class.
Scenario 4: Which should we use for a shared object?
Use static readonly.
public static readonly HttpClient Client = new HttpClient();
Reason: The class exposes one shared HttpClient reference that cannot be replaced after initialization. In ASP.NET Core, IHttpClientFactory is generally preferred for managed HTTP client configuration and lifetimes.
7. Important Interview Follow-up Questions
Q1. Can we assign a const value inside a constructor?
No. A const must be initialized at declaration.
Q2. Can we assign a readonly value inside a constructor?
Yes. An instance readonly field can be assigned in an instance constructor.
Q3. Can we assign a static readonly field inside a normal constructor?
No. It can be assigned at declaration or in a static constructor.
Q4. Can readonly hold a reference type?
Yes.
public readonly List<string> Names = new();
However, readonly prevents replacing the list reference, not modifying the list's contents.
Names.Add("John"); // Allowed
// Names = new List<string>(); // Not allowed after construction
Q5. Which is better for a public constant in a shared library?
Often, static readonly is safer for values that may change between library versions.
A public const value can be embedded into the consuming assembly at compile time. If the library constant changes, consumers may need recompilation to see the new value.
Q6. Is readonly the same as immutable?
No. readonly prevents field reassignment after initialization, but it does not guarantee that an object referenced by the field is immutable.
Quick Interview Revision
|
Keyword |
Remember |
|---|---|
|
|
Compile-time constant; implicitly static |
|
|
Instance field; assign at declaration or constructor |
|
|
Shared class field; assign at declaration or static constructor |
|
|
Must initialize immediately |
|
|
Each object can have a different value |
|
|
One value shared across instances |
|
|
Reference cannot change, but object may still be mutable |
Interview Answer:
const is a compile-time constant that must be initialized at declaration and is implicitly static. readonly is an instance field that can be initialized at declaration or in a constructor, allowing different values for different objects. static readonly is a class-level field that can be initialized at runtime, either at declaration or in a static constructor, and is shared across all instances.