Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> C#

ทำไม Singleton Class ใน C# ต้องเป็น Sealed เสมอ

Singleton Pattern รับประกันว่ามี Instance ของคลาสเพียง Instance เดียวตลอด Lifetime ของ Application

การประกาศ sealed มีความสำคัญ 2 ข้อ:

1. ป้องกัน Inheritance (สาเหตุหลัก)

public class Singleton
{
    private static Singleton _instance;
    private static readonly object _lock = new object();
    
    private Singleton() { } // Private Constructor
    
    public static Singleton Instance
    {
        get
        {
            if (_instance == null)
            {
                lock (_lock)
                {
                    if (_instance == null)
                        _instance = new Singleton();
                }
            }
            return _instance;
        }
    }
}

// หากไม่ sealed - สามารถสร้าง Subclass ได้!
public class DerivedSingleton : Singleton // ❌ ถ้าไม่ sealed จะทำได้
{
    // สร้าง Instance ใหม่ได้อีก!
    public static DerivedSingleton NewInstance => new DerivedSingleton();
}

หากไม่ sealed ผู้อื่นสามารถ:

  • สร้าง Subclass ที่มี Public Constructor
  • สร้าง Instance ใหม่หลาย Instance (破坏 Singleton Guarantee)
  • Override เมธอด/Property เปลี่ยนพฤติกรรม

2. เพิ่มโอกาสให้ Compiler/JIT Optimize

  • sealed บอก Compiler ว่าคลาสนี้ไม่มี Subclass
  • ทำให้ Devirtualization เรียก Method ได้เร็วขึ้น (Non-virtual Call)
  • Inlining ได้ง่ายขึ้น

Singleton แบบ Modern (Thread-safe, Lazy, Sealed)

public sealed class Singleton // ✅ Sealed
{
    // Lazy<T> Thread-safe โดยค่าเริ่มต้น (LazyThreadSafetyMode.ExecutionAndPublication)
    private static readonly Lazy<Singleton> _instance = 
        new Lazy<Singleton>(() => new Singleton());
    
    private Singleton() { } // Private Constructor
    
    public static Singleton Instance => _instance.Value;
    
    public void DoWork() => Console.WriteLine("Singleton working...");
}

การทดสอบ

static void Main()
{
    var s1 = Singleton.Instance;
    var s2 = Singleton.Instance;
    
    Console.WriteLine($"Same instance? {ReferenceEquals(s1, s2)}"); // True
    
    // DerivedSingleton derived = new DerivedSingleton(); // ❌ Compile Error: Cannot derive from sealed type
}

สรุป

  • sealed = ป้องกัน Inheritance = รับประกัน Singleton Property
  • Private Constructor = ป้องกัน Instantiation จากภายนอก
  • ใช้ Lazy<T> สำหรับ Thread-safe Lazy Initialization (แทน Double-Check Locking)
  • ใน DI Container ใช้ services.AddSingleton<T>() แทนการเขียน Singleton เอง