readonly cho phép gán giá trị field tại declaration hoặc trong constructor. Khác với const (compile-time constant), readonly là runtime constant.
4 Context sử dụng readonly
| Context | Ví dụ | Mô tả |
| Field declaration | readonly int _maxSize = 100; | Gán ở khai báo hoặc constructor |
| readonly struct | readonly struct Point { ... } | Struct bất biến (immutable), tất cả field readonly |
| readonly member | public readonly int GetX() => _x; | Method không sửa state (C# 8.0+) |
| ref readonly return | public ref readonly int GetRef() => ref _x; | Trả reference không cho sửa (C# 7.2+) |
Ví dụ: Field readonly
public class Person
{
// Gán tại declaration
private readonly string _createdAt = DateTime.UtcNow.ToString("O");
// Gán trong constructor
private readonly Guid _id;
public string Name { get; set; }
public Person(string name)
{
_id = Guid.NewGuid(); // Chỉ gán được ở đây
Name = name;
}
// Không thể gán _id, _createdAt ở method khác
// public void Reset() => _id = Guid.NewGuid(); // COMPILE ERROR
}
Ví dụ: readonly struct (C# 7.2+)
// Tất cả field tự động readonly, method không thể mutate
public readonly struct Point3D
{
public double X { get; }
public double Y { get; }
public double Z { get; }
public Point3D(double x, double y, double z) => (X, Y, Z) = (x, y, z);
public readonly double DistanceFromOrigin() => Math.Sqrt(X*X + Y*Y + Z*Z);
// Không thể có method non-readonly
}
Lợi ích chính
- Thread Safety: Immutable data an toàn cho multi-threading.
- Compiler Enforcement: Ngăn gán giá trị ngoài constructor (field) hoặc mutate state (struct/method).
- Intent Clarity: Dễ hiểu code: field này không thay đổi sau khởi tạo.
- Optimization: JIT/Runtime có thể tối ưu hóa cho readonly struct (pass by ref readonly).
- Defensive Copy: Tránh defensive copy khi trả về struct lớn (với
ref readonly return).
readonly vs const
| Đặc điểm | const | readonly |
| Thời điểm gán | Compile-time | Runtime (constructor) |
| Kiểu dữ liệu | Primitive, string, enum, null | Bất kỳ kiểu nào |
| Static | Luôn static (implicit) | Instance hoặc static |
| Giá trị | Cố định suốt đời chương trình | Có thể khác nhau per instance |
| Versioning | Rủi ro (nhúng vào caller assembly) | An toàn (đọc từ field tại runtime) |
Best Practice
- Dùng
readonly cho field không thay đổi sau ctor.
- Dùng
readonly struct cho struct đại diện giá trị bất biến (Point, Vector, DateRange...).
- Dùng
readonly cho method không mutate state.
- Dùng
ref readonly return cho struct lớn readonly.
- Tránh
const cho giá trị có thể thay đổi giữa các version (connection string, config...).