Singleton Pattern đảm bảo một class chỉ có duy nhất một instance trong toàn ứng dụng. Trong C#, từ khóa sealed đóng vai trò quan trọng để bảo vệ tính singleton.
Tại sao cần sealed?
- Ngăn chặn kế thừa: Nếu class không sealed, class con có thể kế thừa và tạo nhiều instance của class con -> vi phạm Singleton.
- Constructor private không đủ: Constructor private chỉ ngăn
new Singleton()từ bên ngoài. Class con vẫn có thể gọibase()trong constructor của nó (nếu base có constructor private, class con không thể gọi -> compile error). Nhưng nếu base có constructorprotectedhoặcinternal, class con có thể tạo instance. - Bảo vệ tại compile-time:
sealedbáo lỗi ngay khi ai đó cố gắngclass Derived : Singleton.
Ví dụ: Singleton đúng chuẩn (Thread-safe, Lazy, Sealed)
public sealed class Singleton
{
private static readonly Lazy<Singleton> _instance =
new Lazy<Singleton>(() => new Singleton(), LazyThreadSafetyMode.ExecutionAndPublication);
private Singleton()
{
Console.WriteLine("Singleton instance created");
}
public static Singleton Instance => _instance.Value;
public void DoWork() => Console.WriteLine("Working...");
}
// Client
class Program
{
static void Main()
{
var s1 = Singleton.Instance;
var s2 = Singleton.Instance;
Console.WriteLine($"Same instance? {ReferenceEquals(s1, s2)}"); // True
// Lỗi compile nếu bỏ comment dòng dưới:
// class DerivedSingleton : Singleton { } // Error: Cannot derive from sealed type
Console.ReadLine();
}
}
Nếu không sealed - Demo vi phạm
// KHÔNG SEALED - Nguy hiểm
public class BadSingleton
{
private static BadSingleton _instance;
private BadSingleton() { }
public static BadSingleton Instance => _instance ??= new BadSingleton();
}
// Kẻ tấn công
public class DerivedBad : BadSingleton
{
public DerivedBad() { } // Có thể tạo instance mới!
}
class Program
{
static void Main()
{
var s1 = BadSingleton.Instance;
var s2 = new DerivedBad(); // Instance thứ 2!
var s3 = new DerivedBad(); // Instance thứ 3!
Console.WriteLine($"s1 == s2? {ReferenceEquals(s1, s2)}"); // False
}
}
Các cách triển khai Singleton khác (đều nên sealed)
| Cách | Đặc điểm |
|---|---|
| Lazy<T> (khuyến nghị) | Thread-safe, lazy, đơn giản, hiệu năng tốt. |
| Double-check locking | Manual, dễ sai, cũ. |
| Static constructor | Eager initialization, thread-safe. |
| Nested class | Lazy, thread-safe (trước C# 4.0). |
Kết luận: Luôn đánh dấu sealed cho Singleton class. Nó là rào chắn cuối cùng ngăn chặn việc phá vỡ pattern bằng kế thừa.