隨手 Design Pattern (6) - 單例模式 (Singleton Pattern)

單例模式是軟件工程中最著名的模式之一。從本質上講,單例是一個只允許創建自身的單個實例的類,通常可以簡單地訪問該實例。最常見的是,單例在創建實例時不允許指定任何參數 - 否則對實例的第二個請求但具有不同的參數可能會有問題!(如果對於具有相同參數的所有請求,應訪問相同的實例,則工廠模式更合適。)本文僅處理不需要參數的情況。通常,單例的要求是它們是懶惰地創建的 - 即在第一次需要之前不創建實例。

總結 Singleton 模式有兩個特點

  • 單一實例 (Single Instance)
  • 延遲建立實例 (Lazy Instantiation)

C# 實作

在C#中實現單例模式有各種不同的方法。從最常見的,不是執行緒安全的,以及完全延遲加載,安全,簡單且高性能的版本開始。

而有此共同特徵:

  • 單個構造函數,它是私有且無參數的。這可以防止其他類實例化它(這將違反模式)。(C#來說類別必須 sealed ,且 建構式為 private)
  • 一個靜態變量,用於保存對單個已創建實例的引用。(C# 來說需要一個 publicstatc 的 )
  • 公共靜態意味著獲取對單個創建實例的引用,必要時創建一個實例。(C# 來說類別需 publicstatic)

非執行緒安全 Singleton

這是個不好的案例,此方式不是執行緒安全,無法確保在多執行緒情況下是唯一的實例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public sealed class NotThreadSafeSingleton
{
private static NotThreadSafeSingleton _instance = null;

private NotThreadSafeSingleton()
{
}

public static NotThreadSafeSingleton Instance
{
get
{
if (_instance == null)
{
_instance = new NotThreadSafeSingleton();
}

return _instance;
}
}
}

簡單執行緒安全 Singleton

使用 Lock 來確保執行緒安全。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    private static readonly object padlock = new object();

private static SimpleThreadSafetySingleton _instance = null;

private SimpleThreadSafetySingleton()
{
}

public static SimpleThreadSafetySingleton Instance
{
get
{
lock (padlock)
{
if (_instance == null)
{
_instance = new SimpleThreadSafetySingleton();
}
return _instance;
}
}
}
}

使用 Double-checked Locking 確保執行緒安全

使用 Double-checked Locking 確保執行緒安全。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public sealed class DoubleCheckedLockingSingleton
{
private static readonly object padlock = new object();
private static DoubleCheckedLockingSingleton _instance = null;

//用來LOCK建立instance的程序。
public static DoubleCheckedLockingSingleton Instance
{
get
{
if (_instance == null)
{
lock (padlock)
{
if (_instance == null)
{
_instance = new DoubleCheckedLockingSingleton();
}
}
}

return _instance;
}
}
}

不使用 Lock, 確保執行緒安全 (非 Lazy)

此範例沒有使用 Lock, 而仍是執行緒安全的 Singleton,但不是 Lazy。

1
2
3
4
5
6
7
8
9
    public sealed class EagerSingleton
{
private EagerSingleton()
{
}

public static EagerSingleton Instance { get; } = new EagerSingleton();
}
}

完整 Lazy 實例

此範例達成了延遲建立。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public sealed class LazySingleton
{
private LazySingleton()
{
}

public static LazySingleton Instance
{
get
{
return InnerClass.Instance;
}
}

private class InnerClass
{
internal static readonly LazySingleton Instance = new LazySingleton();

static InnerClass()
{
}
}
}

使用 .NET 4 的 Lazy<T>

此方式使用後 .Net Framework 4 之後提供的 Lazy<T>,如此可以簡單的達成 Singleton 要求的 唯一延遲建立

1
2
3
4
5
6
7
8
9
10
public sealed class DotNet4LazySingleton
{
private static readonly Lazy<DotNet4LazySingleton> lazy = new Lazy<DotNet4LazySingleton>(() => new DotNet4LazySingleton());

private DotNet4LazySingleton()
{
}

public static DotNet4LazySingleton Instance { get { return lazy.Value; } }
}

結語

除了第一個範例不建議之外,其餘的是可以依實際情況斟酌使用。

程式碼範例