單例模式是設計模式(Design Patterns)的其中一種,在這裡,我們將透過底下程式碼,展示如何實作出單例模式的類別,並且檢測這樣的設計是否正確可以運行。
了解更多關於 [C# 程式設計手冊]
展示專案原始碼
/// <summary>
/// 單例模式 (Singleton Pattern) 的類別設計展示
/// </summary>
public class Singleton
{
/// <summary>
/// 這個欄位為私有,用來持有唯一的執行實例(Instance)
/// </summary>
private static Singleton instance;
public DateTime GenerateTime { get; set; }
/// <summary>
/// 建構式,可以在這裡做是到的實例物件的初始化動作
/// 不過,這樣預設建構式的存取修飾詞為 private,也就是,讓何人都無法使用 new 運算子產生這個類別的物件,只有他自己本身可以
/// </summary>
private Singleton()
{
GenerateTime = DateTime.Now;
}
/// <summary>
/// 取得這個類別的單例物件 (Singleton Instance)的屬性設計說明
/// 這個屬性是唯讀,也就是只有讀取功能,無法做任何設定(因為沒有 set 屬性存取子)
/// 當然,您也可以使用一個靜態方法,取得這個類別的單例物件
/// </summary>
public static Singleton Instance
{
get
{
// 若 instance 並沒有持有一個單例物件,則需要在這個時候,進行產生出來
// ?? 若這個 單例物件 需要能夠在多執行緒環境下正確執行,又該如何設計呢?
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("請稍後四秒鐘,正在產生四個單例物件中...");
// 取得這個類別的單例物件
Singleton singleton1 = Singleton.Instance;
Thread.Sleep(1000);
Singleton singleton2 = Singleton.Instance;
Thread.Sleep(1000);
Singleton singleton3 = Singleton.Instance;
Thread.Sleep(1000);
Singleton singleton4 = Singleton.Instance;
Thread.Sleep(1000);
Console.WriteLine($"單例物件1 HashCode: {singleton1.GetHashCode()} {singleton1.GenerateTime} 的產生時間識別");
Console.WriteLine($"單例物件2 HashCode: {singleton2.GetHashCode()} {singleton2.GenerateTime} 的產生時間識別");
Console.WriteLine($"單例物件3 HashCode: {singleton3.GetHashCode()} {singleton3.GenerateTime} 的產生時間識別");
Console.WriteLine($"單例物件4 HashCode: {singleton4.GetHashCode()} {singleton4.GenerateTime} 的產生時間識別");
if(singleton1 == singleton2 )
{
if (singleton2 == singleton3)
{
if (singleton3 == singleton4)
{
Console.WriteLine($"這四個物件變數,指向同一個物件記憶體位置的單例物件");
}
}
}
Console.WriteLine("Press any key for continuing...");
Console.ReadKey();
}
}
單例模式設計說明
在這裡我們說明這個單例模式的類別該如何設計:
- 私有 Private 建構式在單例模式中,使用這將無法使用 new 運算子來產生這個類別的物件,我們必須將這樣的需求給他禁止,因此,我們在預設建構函式的存取修飾詞(Access Modifier)設定成為
private
。
private Singleton()
{
GenerateTime = DateTime.Now;
}
- 使用屬性來取得唯一的執行實體(Instance)接著,我們建立一個 Instance 屬性與 instance 私有欄位,使用者僅能夠透過這個 Instance 屬性來取得這個類別的執行實體,而且,這個屬性屬於唯讀,也就是說,使用者沒有任何權力來由外部設定這個 instance 私有欄位的內容。在這個 get屬性存取子中,我們會先判斷私有欄位 instance 這個物件欄位變數是否有任何物件存在,若該物件欄位變數為空值 (null),我們將會使用
instance = new Singleton();
這樣的敘述,產生這個單例物件。在這裡,我們看到了我們使用了 new 運算子呼叫了預設建構函式,我們可以這樣做,這是因為,我們是在這個類別裡面的 get屬性存取來執行這樣的敘述,這是可以的,因為,預設建構函式設定其存取子修飾詞為 private,是限制不允許從類別以外的地方來呼叫這個建構函式,但是,卻必須可以在該類別裡面來呼叫這個私有的建構函式。
public static Singleton Instance
{
get
{
// 若 instance 並沒有持有一個單例物件,則需要在這個時候,進行產生出來
// ?? 若這個 單例物件 需要能夠在多執行緒環境下正確執行,又該如何設計呢?
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
底下則是這個展示範例專案的執行結果,我們可以看的出來,透過
Singleton.Instance
所取得的物件,都是同一個。請稍後四秒鐘,正在產生四個單例物件中...
單例物件1 HashCode: 46104728 2017/9/3 下午 10:17:24 的產生時間識別
單例物件2 HashCode: 46104728 2017/9/3 下午 10:17:24 的產生時間識別
單例物件3 HashCode: 46104728 2017/9/3 下午 10:17:24 的產生時間識別
單例物件4 HashCode: 46104728 2017/9/3 下午 10:17:24 的產生時間識別
這四個物件變數,指向同一個物件記憶體位置的單例物件
Press any key for continuing...