ความแตกต่างหลัก
| คุณสมบัติ | const | readonly |
|---|---|---|
| Evaluation Time | Compile-time Constant | Runtime Constant |
| Value Assignment | ต้อง Assign ตอน Declaration | Assign ได้ใน Declaration หรือ Constructor |
| Modifiable | ไม่สามารถเปลี่ยนได้เลย | เปลี่ยนได้ใน Constructor (Instance) |
| Scope | Implicitly Static | Instance หรือ Static |
| Type Restrictions | Built-in Types, String, Enum, Null | Any Type (Class, Struct, Array, etc.) |
| In Method | ได้ (Local Constant) | ไม่ได้ (Field Only) |
| Versioning | ค่า Embedded ใน Caller Assembly | อ่านจาก Assembly ตอน Runtime |
const - Compile-time Constant
public class Constants
{
// ต้องเป็น Constant Expression (Compile-time Known)
public const double PI = 3.14159265359;
public const int MaxRetries = 3;
public const string AppName = "MyApp";
// ❌ Error: Not a constant expression
// public const DateTime BuildDate = DateTime.Now;
// public const List Numbers = new List { 1, 2 };
}
// Usage - ค่าถูก Inline เข้า Caller IL
void Test()
{
Console.WriteLine(Constants.PI); // IL: ldc.r8 3.14159...
}
readonly - Runtime Constant
public class Config
{
// Instance Readonly - Assign ใน Constructor
public readonly string ConnectionString;
public readonly int MaxConnections;
public readonly List AllowedHosts;
public Config(string env)
{
// Runtime Logic ได้
ConnectionString = LoadFromConfig(env);
MaxConnections = env == "Prod" ? 100 : 10;
AllowedHosts = LoadHostsFromDb(); // Complex Logic
}
// Static Readonly - Assign ใน Static Constructor
public static readonly string Version;
static Config()
{
Version = Assembly.GetExecutingAssembly().GetName().Version.ToString();
}
}
Versioning Issue (สำคัญมาก!)
⚠️ const = Versioning Problem
Library v1:
App Compile → IL:
Library v2:
App Re-run ไม่ Recompile → ยังใช้ 100!
readonly = Safe
Library v1:
App Run → อ่านค่าจาก Library DLL ตอน Runtime → ได้ 200 เมื่อ Update DLL
Library v1:
public const int Max = 100;App Compile → IL:
ldc.i4.s 100 (ค่าถูก Burn ใน App)Library v2:
public const int Max = 200;App Re-run ไม่ Recompile → ยังใช้ 100!
readonly = Safe
Library v1:
public static readonly int Max = 100;App Run → อ่านค่าจาก Library DLL ตอน Runtime → ได้ 200 เมื่อ Update DLL
ตัวอย่าง Versioning
// Library.dll v1
public class Settings
{
public const int ConstMax = 100;
public static readonly int ReadonlyMax = 100;
}
// App.exe references Library.dll
class Program
{
static void Main()
{
Console.WriteLine(Settings.ConstMax); // 100 (Burned in App IL)
Console.WriteLine(Settings.ReadonlyMax); // 100 (Read from Library)
}
}
// Update Library.dll v2 (ไม่ Recompile App)
// ConstMax = 200, ReadonlyMax = 200
// Run App.exe อีกครั้ง:
// ConstMax: 100 (เก่า!)
// ReadonlyMax: 200 (ใหม่!)
Local Constants (ใน Method)
void Calculate()
{
const double TaxRate = 0.07; // Local Const - OK
// readonly double Rate = 0.07; // ❌ Error: readonly not valid on local
const int BufferSize = 4096;
// ...
}
Best Practice
- ✅ const: ค่าคงที่จริง (Math Constants, Enum-like, Protocol Values) ที่ไม่เปลี่ยนแปลงตลอดกาล
- ✅ readonly: Configuration, Settings, Injected Dependencies, Complex Objects, ค่าที่อาจเปลี่ยน Version
- ✅ static readonly: Singleton, Shared Configuration, Expensive Initialization
- ✅ ใช้
constสำหรับdefaultParameter Values - ❌ อย่าใช้ const สำหรับ Version Numbers, Feature Flags, Config Values
Modern Alternative: init-only Properties (C# 9+)
public class Settings
{
// Immutable after construction, รองรับ Object Initializer
public int MaxConnections { get; init; } = 100;
public string ConnectionString { get; init; } = "";
// Required (C# 11+)
public required string ApiKey { get; init; }
}