const vs readonly ใน C#
ทั้งคู่ใช้สำหรับค่าที่ **ไม่เปลี่ยนแปลง** (immutable) หลังจากกำหนด แต่มีขอบเขตและเวลาที่กำหนดค่าแตกต่างกัน
const (Constant) - ค่าคงที่ในเวลาคอมไพล์
- ต้องกำหนดค่า **ตอนประกาศ** (compile-time constant)
- เป็น
staticโดยอัตโนมัติ (ไม่ต้องเขียน static) - ค่าต้องเป็น compile-time constant (literal, const อื่น, หรือ expression ที่คำนวณได้ตอน compile)
- ไม่สามารถเปลี่ยนค่าได้เด็ดขาด
- เหมาะสำหรับค่าที่ไม่มีวันเปลี่ยน: PI, จำนวนวันในสัปดาห์, App name
class Constants
{
public const double PI = 3.14159265359;
public const int DaysInWeek = 7;
public const string AppName = "MyApp";
// ❌ ไม่ได้: const int x = SomeMethod(); // ต้องเป็น compile-time
// ❌ ไม่ได้: const DateTime Now = DateTime.Now; // ไม่ใช่ constant
}
readonly - ค่าคงที่ในเวลารันไทม์
- กำหนดค่าได้ **ตอนประกาศ** หรือ **ใน Constructor** เท่านั้น
- สามารถเป็น **instance member** (แต่ละ instance ค่าต่างกันได้) หรือ
static readonly - ค่าสามารถมาจาก runtime calculation, config, database, dependency injection
- เหมาะสำหรับค่าที่ตั้งค่าตอนรัน: connection string, config, injected services
class Settings
{
// Instance readonly - ตัวแต่ละ instance ค่าต่างกันได้
public readonly string ConnectionString;
public readonly int MaxRetries;
// Static readonly - ค่าร่วมกันทุก instance
public static readonly DateTime StartupTime = DateTime.Now;
public Settings(string connStr, int retries = 3)
{
ConnectionString = connStr; // กำหนดใน constructor
MaxRetries = retries;
}
// ✅ สามารถใช้ method/property ในการคำนวณ
public readonly int Timeout = GetDefaultTimeout();
static int GetDefaultTimeout() => 30;
}
ตารางเปรียบเทียบ
| คุณสมบัติ | const | readonly |
|---|---|---|
| เวลากำหนดค่า | Compile-time | Runtime (declaration หรือ constructor) |
| Static/Instance | Static เท่านั้น (implicit) | Static หรือ Instance ได้ |
| ชนิดข้อมูลที่อนุญาต | Primitive, string, enum, null, reference type (เฉพาะ null) | ทุกชนิด (class, struct, array, etc.) |
| การเข้าถึง | ClassName.CONST_NAME | instance.ReadonlyField หรือ ClassName.StaticReadonly |
| Versioning (DLL) | ค่า embed ใน calling assembly | อ่านจาก defining assembly รันไทม์ |
| Performance | เร็วกว่าเล็กน้อย (inline) | เข้าถึง field ปกติ |
Versioning Issue - สำคัญ!
// Assembly A (Library)
public class Config
{
public const int MaxConnections = 100; // Compile-time
public static readonly int DefaultTimeout = 30; // Runtime
}
// Assembly B (Consumer) - ใช้ Config
// ถ้า Assembly A เปลี่ยน MaxConnections = 200 และ rebuild แค่ A
// Assembly B ยังคงเห็น 100 (เพราะ embed ตอน compile B)
// แต่ DefaultTimeout จะเห็น 30 ใหม่ (อ่าน runtime)
แนวทางเลือกใช้
// ใช้ const เมื่อ:
// - ค่าเป็นค่าคงที่จริง ไม่มีวันเปลี่ยน
// - เป็น primitive/string/enum
// - ต้องการ performance เล็กน้อย
public const int MaxBufferSize = 4096;
// ใช้ readonly เมื่อ:
// - ค่ามาจาก config, DI, runtime calculation
// - เป็น reference type (array, object, List)
// - ต้องการ instance-specific values
// - อาจเปลี่ยนใน version ถัดไป (versioning safety)
public readonly string[] AllowedOrigins;
public readonly ILogger Logger;
public MyClass(IConfiguration config, ILogger logger)
{
AllowedOrigins = config.GetSection("Cors:Origins").Get();
Logger = logger;
}
C# 11: required keyword
class Config
{
public required string ConnectionString { get; init; } // ต้อง set ตอนสร้าง object
public required int Port { get; init; }
}
// ใช้งาน - compile error ถ้าไม่ set
var cfg = new Config { ConnectionString = "...", Port = 5432 };