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

單例模式在 C# 中的實作詳解(下):延遲初始化進階技巧與擴展應用

     編輯:關於C語言

延遲初始化的同步驗證結果

使用 [MethodImpl(MethodImplOptions.Synchronized)] 或 lock 修飾 Instance 方法後,四執行緒並發測試最終計數穩定為 16,無重複初始化問題。每次執行順序因排程不同而異,但結果正確。

.NET 4.0+ 現代最佳解:Lazy<T>

public sealed class Singleton {
    private static readonly Lazy<Singleton> lazy = 
        new Lazy<Singleton>(() => new Singleton(), LazyThreadSafetyMode.ExecutionAndPublication);
    private Singleton() { }
    public static Singleton Instance { get { return lazy.Value; } }
}
  • ExecutionAndPublication:確保所有執行緒看到同一實例(預設模式)
  • PublicationOnly:允許競爭建立,取第一個完成者(適合低成本建構)
  • None:非執行緒安全,單執行緒場景用

優點:語意清晰、效能優異、支援例外快取(建構失敗後重試)、可序列化。

靜態建構子替代方案

public sealed class Singleton {
    private static readonly Singleton instance;
    static Singleton() { instance = new Singleton(); }  // CLR 保證執行一次
    private Singleton() { }
    public static Singleton Instance { get { return instance; } }
}
  • 延遲至首次存取靜態成員時觸發
  • 無需額外同步程式碼
  • 缺點:若類別有其他靜態成員被先存取,會提前初始化

單例模式擴展:多例模式

限制實例數量上限為 N(如資料庫連線池、執行緒池):

public sealed class MultiTon {
    private static readonly Dictionary instances = new Dictionary();
    private static readonly object syncLock = new object();
    private int id;
    private MultiTon(int id) { this.id = id; }
    
    public static MultiTon GetInstance(int key) {
        string k = key.ToString();
        lock (syncLock) {
            if (!instances.ContainsKey(k))
                instances[k] = new MultiTon(key);
            return instances[k];
        }
    }
    public int Id { get { return id; } }
}

依賴注入容器中的單例

現代架構建議由容器管理生命週期,而非類別自行實作單例:

// ASP.NET Core 範例
services.AddSingleton<IMyService, MyService>();  // 容器保證單例

// 建構子注入
public class Consumer {
    private readonly IMyService service;
    public Consumer(IMyService service) { this.service = service; }
}
  • 解耦:類別不知自己是單例,易測試(可注入 Mock)
  • 統一生命週期管理
  • 支援範圍、暫時等多種模式

單例的反模式與風險

總結建議

  1. 首選:Lazy<T> 或靜態建構子(.NET 4.0+)
  2. 避免:手寫 lock/double-check(除非舊版框架)
  3. 架構層面:改用 DI 容器管理單例生命週期
  4. 設計原則:單例應為無狀態服務,避免可變全域狀態
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
問題說明緩解
隱藏依賴類別間耦合不透明介面抽象 + DI 容器
難以單元測試狀態殘留、難以 Mock改用 DI、提供 Reset()
全域狀態副作用平行測試干擾避免可變靜態狀態
序列化破壞單一性反序列化產生新實例實作 ISerializable 控制