readonly เป็น Modifier สำหรับ Field (Field Declaration) ที่บ่งบอกว่า Field นี้สามารถกำหนดค่าได้เฉพาะ:
- ตอนประกาศ (Declaration)
- ใน Constructor ของคลาสเดียวกัน (Instance Constructor หรือ Static Constructor)
ต่างจาก const ที่ต้องเป็น Compile-time Constant และเป็น Static โดยอัตโนมัติ
4 Contexts ของ readonly
| Context | Syntax | คำอธิบาย |
|---|---|---|
| Field Declaration | readonly int _maxSize = 100; |
Instance หรือ Static Field |
| Readonly Struct | readonly struct Point { ... } |
Struct ทั้งหมด Immutable (C# 7.2+) |
| Readonly Member | readonly void Method() / readonly int Prop => _field; |
Method/Property ไม่ Mutate State (C# 8.0+) |
| Ref Readonly Return | ref readonly int GetRef() |
Return Ref แต่ไม่อนุญาตให้แก้ไข (C# 7.2+) |
ตัวอย่าง: readonly Field
public class Configuration
{
// 1. Init ที่ Declaration
private readonly string _appName = "MyApp";
// 2. Init ใน Constructor (Runtime Value)
private readonly string _connectionString;
private readonly DateTime _createdAt;
public Configuration(string connStr)
{
_connectionString = connStr; // OK
_createdAt = DateTime.UtcNow; // OK - Runtime Value
// _appName = "New"; // ❌ Compile Error: Readonly
}
// Readonly Property (Expression-bodied)
public string ConnectionString => _connectionString; // Readonly by nature
public DateTime CreatedAt => _createdAt;
}
ตัวอย่าง: readonly Struct (C# 7.2+)
// Struct ทั้งชิ้น Immutable - ป้องกัน Accidental Mutation
public readonly struct Point3D
{
public readonly double X; // Field ต้อง readonly ด้วย
public readonly double Y;
public readonly double Z;
public Point3D(double x, double y, double z)
{
X = x; Y = y; Z = z;
}
// Method ต้องเป็น readonly ด้วย (ไม่สามารถ Mutate ได้)
public readonly double DistanceFromOrigin() => Math.Sqrt(X*X + Y*Y + Z*Z);
// Property ได้ปกติ (แต่ไม่ควรมี Setter)
public double Magnitude => DistanceFromOrigin();
}
ตัวอย่าง: readonly Member (C# 8.0+)
public class Counter
{
private int _count;
// Method นี้รับประกันว่าไม่ Mutate State
public readonly int GetCount() => _count;
// Property แบบ Readonly
public readonly int Count => _count;
// Method ที่ Mutate - ไม่สามารถเป็น readonly
public void Increment() => _count++;
}
ตัวอย่าง: ref readonly Return (C# 7.2+)
public class LargeStructContainer
{
private readonly LargeStruct _data = new LargeStruct(); // สมมติใหญ่
// คืน Reference แต่ไม่อนุญาตให้แก้ไข (Performance: No Copy)
public ref readonly LargeStruct GetData() => ref _data;
}
// ใช้งาน
var container = new LargeStructContainer();
ref readonly var data = ref container.GetData();
// data.SomeField = 10; // ❌ Compile Error: Cannot assign to readonly variable
var copy = data; // OK: Copy ค่าไป (ถ้าต้องการแก้)
ประโยชน์หลัก
- Immutability Guarantee: Compiler Enforce ไม่ให้เปลี่ยนค่าหลัง Constructor
- Thread Safety: Read-only Data ปลอดภัยต่อ Thread โดยธรรมชาติ
- Intent Clarity: บอก Maintainer ว่า Field นี้ "Set Once"
- Compiler Optimizations: JIT อาจ Optimize ได้ดีขึ้น (Constant Propagation)
- Defensive Copy Prevention:
ref readonlyหลีกเลี่ยง Copy Struct ใหญ่
readonly vs const
| Feature | const |
readonly |
|---|---|---|
| Value Set | Compile-time | Runtime (Constructor) |
| Static | Implicit Static | Instance หรือ Static |
| Type | Primitive/String/Enum/Null only | Any Type |
| Versioning | Burn into Caller IL (Breaking Change) | Runtime Lookup (Safe) |
Best Practice
- ใช้
readonlyเป็น Default สำหรับ Fields ที่ไม่ควรเปลี่ยนหลัง Init - ใช้
readonly structสำหรับ Immutable Value Types - ใช้
readonlyMethods/Properties เพื่อ Document Intent - หลีกเลี่ยง
constสำหรับค่าที่อาจเปลี่ยน (Version, Config, Connection String)