Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> C#

Tại sao Singleton class trong C# thường được sealed?

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?

  1. 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.
  2. Constructor private không đủ: Constructor private chỉ ngăn new Singleton() từ bên ngoài. Class con vẫn có thể gọi base() 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ó constructor protected hoặc internal, class con có thể tạo instance.
  3. Bảo vệ tại compile-time: sealed báo lỗi ngay khi ai đó cố gắng class 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 lockingManual, dễ sai, cũ.
Static constructorEager initialization, thread-safe.
Nested classLazy, 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.