單例模式確保一個類別在整個應用程式生命週期中只有一個實例,並提供全域存取點。它是最基礎也最常被誤用的模式之一。本文深度解析單例模式的多種實現方式,從經典雙重檢查鎖定到 .NET 專用的巢狀類別靜態初始化,再到泛型單例基類,並對比執行緒安全性、延遲初始化、效能與序列化/反射攻擊防護。
public sealed class Singleton
{
private static readonly Singleton _instance = new Singleton();
private Singleton() { }
public static Singleton Instance => _instance;
}
public sealed class Singleton
{
private static volatile Singleton _instance;
private static readonly object _lock = new object();
private Singleton() { }
public static Singleton Instance
{
get
{
if (_instance == null) // 第一次檢查:無鎖快速路徑
{
lock (_lock)
{
if (_instance == null) // 第二次檢查:確保單一建立
_instance = new Singleton();
}
}
return _instance;
}
}
}
volatile 語義易誤解、效能稍遜於靜態初始化。
public class Singleton<T> where T : new()
{
public static T Instance => SingletonCreator.instance;
private class SingletonCreator
{
static SingletonCreator() { } // 觸發延遲初始化
internal static readonly T instance = new T();
}
}
Instance 時觸發)、執行緒安全(CLR 保證)、零鎖零判斷、泛型複用、程式碼極簡。SingletonCreator 的靜態建構子在首次存取 instance 時由 CLR 執行一次,天然執行緒安全且延遲。
public sealed class Singleton
{
private static readonly Lazy<Singleton> _instance =
new Lazy<Singleton>(() => new Singleton(), LazyThreadSafetyMode.ExecutionAndPublication);
private Singleton() { }
public static Singleton Instance => _instance.Value;
}
namespace Pattern.Singleton
{
public class Singleton<T> where T : new()
{
public static T Instance => SingletonCreator.instance;
class SingletonCreator
{
internal static readonly T instance = new T();
}
}
}
where T : new() 約束確保 T 有無參建構子。SingletonCreator 為 private 隔離實現細節。internal static readonly 確保單一賦值、組件內可見(測試友善)。
protected void Page_Load(object sender, EventArgs e)
{
// 使用單例模式
Response.Write(Singleton<Test>.Instance.Time); // 第一次存取 → 建構 Test (Sleep 3秒)
Response.Write(Singleton<Test>.Instance.Time); // 第二次存取 → 同一實例,時間相同
// 不用單例模式
Test t = new Test(); // 每次新建構 → Sleep 3秒
Response.Write(t.Time);
Test t2 = new Test(); // 再次新建構 → Sleep 3秒
Response.Write(t2.Time);
}
public class Test
{
private DateTime _time;
public Test() { Thread.Sleep(3000); _time = DateTime.Now; }
public string Time => _time.ToString();
}
2007-2-10 22:35:11 ← 單例第一次存取,耗時 3 秒建構
2007-2-10 22:35:11 ← 單例第二次存取,瞬間返回,時間相同
2007-2-10 22:35:14 ← 非單例第一次新建,耗時 3 秒
2007-2-10 22:35:17 ← 非單例第二次新建,再耗時 3 秒
完美驗證:單例模式確保全域唯一實例,建構成本僅付出一次;非單例每次新建構付出完整成本。
// 反射繞過私有建構子
var constructors = typeof(Singleton).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance);
var instance = constructors[0].Invoke(null); // 建立第二個實例!
// 防護:建構子檢查
private Singleton()
{
if (_instance != null) // 或 SingletonCreator.instance != null
throw new InvalidOperationException("單例已存在,禁止反射建構");
}
[Serializable]
public sealed class Singleton : ISerializable
{
// ...
protected Singleton(SerializationInfo info, StreamingContext context) { }
public void GetObjectData(SerializationInfo info, StreamingContext context) { }
// 反序列化時回傳現有實例
public object GetRealObject(StreamingContext context) => Instance;
}
services.AddSingleton<TService, TImpl>():容器管理生命週期,單例範圍為容器實例(通常為應用程式)。| 特性 | 單例 | 靜態類別 |
|---|---|---|
| 介面實作 | 可實作介面 | 不可 |
| 繼承 | 可繼承 (非 sealed) | 不可 |
| 延遲初始化 | 支援 | 僅靜態建構子 (類別載入時) |
| 參數化建構 | 可透過工廠/建構子參數 | 不可 |
| 序列化 | 可控制 | 不可 |
| DI 容器管理 | 天然支援 | 需包裝 |