首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 單例模式完全指南:泛型實現、執行緒安全與效能優化

     編輯:關於C#

前言

單例模式確保一個類別在整個應用程式生命週期中只有一個實例,並提供全域存取點。它是最基礎也最常被誤用的模式之一。本文深度解析單例模式的多種實現方式,從經典雙重檢查鎖定到 .NET 專用的巢狀類別靜態初始化,再到泛型單例基類,並對比執行緒安全性、延遲初始化、效能與序列化/反射攻擊防護。

核心實現方式對比

1. 惡漢式:靜態初始化(.NET 推薦基準)

public sealed class Singleton
{
    private static readonly Singleton _instance = new Singleton();
    private Singleton() { }
    public static Singleton Instance => _instance;
}
  • 優點:簡單、執行緒安全(CLR 靜態建構子保證)、效能極高(無鎖、無判斷)。
  • 缺點:非延遲初始化(類別載入時即建立),若建構耗時/資源大且未必用到則浪費。

2. 懶漢式:雙重檢查鎖定


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 語義易誤解、效能稍遜於靜態初始化。
  • 注意:.NET Framework 4.0+ 記憶體模型保證 DCL 正確,但舊版/某些架構需謹慎。

3. 巢狀類別靜態初始化:完美平衡(本範例採用)


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 執行一次,天然執行緒安全且延遲。
  • 此為 .NET 獨有的優雅模式,強烈推薦作為預設選擇。

4. Lazy<T> 內建支援


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;
}
  • 優點:框架內建、語義清晰、支援多種執行緒安全模式、例外快取(建構失敗可重試)。
  • 適用:.NET 4.0+ 專案,無需手寫樣板碼。

範例程式碼深度解析

泛型單例基類


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>():容器管理生命週期,單例範圍為容器實例(通常為應用程式)。
  • 優點:可測試(Mock)、支援介面/實現分離、生命週期統一管理、避免靜態狀態污染測試。
  • 現代 .NET 應用**首選 DI 容器單例**,而非手寫靜態單例類別。

分散式系統中的「單例」

  • 單進程單例 ≠ 分散式單例。
  • 分散式鎖、領導者選舉、一致性雜湊、單分區處理等模式解決跨節點唯一性。

單例模式 vs 靜態類別

最佳實務總結

  1. 首選:services.AddSingleton() (DI 容器) — 現代 .NET 應用標準。
  2. 次選:巢狀類別泛型單例 Singleton<T> — 庫/框架作者、無 DI 環境、需泛型複用。
  3. 備選:Lazy<T> — 明確語義、例外快取需求。
  4. 避免:手寫雙重檢查鎖定、單純靜態類別替代單例、公有建構子類別假裝單例。
  5. 防護:sealed 類別、私有建構子、反射/序列化防護、文件化單例範圍(進程/容器/請求)。

總結

單例模式看似簡單,實則蘊含執行緒安全、延遲初始化、記憶體模型、反射/序列化防護、DI 整合等深度議題。本範例展示了巢狀類別泛型單例這一 .NET 獨有的優雅實現,完美平衡了延遲初始化、執行緒安全、零鎖效能與程式碼簡潔性。實務中,隨著依賴注入成為標配,「手寫單例類別」逐漸退出歷史舞台,取而代之的是「容器管理單例生命週期」。然而,理解單例模式的核心機制與陷阱,對於設計高併發系統、框架庫、避免靜態狀態濫用、正確使用 DI 容器仍具備根本指導意義。掌握從惡漢式到 Lazy<T>、從巢狀類別到 DI 容器的演進脈絡,能讓你在面對「全域唯一實例」需求時,做出最適合當前架構上下文的技術決策。


參考資源:DoFactory Singleton Pattern

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
特性單例靜態類別
介面實作可實作介面不可
繼承可繼承 (非 sealed)不可
延遲初始化支援僅靜態建構子 (類別載入時)
參數化建構可透過工廠/建構子參數不可
序列化可控制不可
DI 容器管理天然支援需包裝