一個類別只有一個實體化的物件(Instance),取得物件有統一的方法。
singleton 基本特點:
- sealed class
- private constructor without parameter
- static variable hold instance
- public static method or property get instance variable
sealed 這個關鍵字表示這個 class 不能被繼承。
private constructor 表示這個 class 不會被其他地方建立 instance。
其實不寫以上兩點程式也可以正常運作,但有寫的話可以讓閱讀程式碼的人一眼就知道這個 class 是 singleton,尤其多人開發時,保持良好的寫作習慣才能避免挖坑給別人跳。
singleton 有很多種寫法
第一種:貪婪單例模式(Greed Singleton or Eager Singleton) – thread safe
public sealed class GreedySingleton
{
private static readonly GreedySingleton _instance = new GreedySingleton();//程式一啟動就建立instance
//Explicit static consturctor to tell C# compiler
//not to mark type as beforefieldinit
static GreedySingleton() { }
private GreedySingleton() { }
public static GreedySingleton GetInstance()
{
return _instance;
}
}
貪婪單例的缺點是程式一啟動就建立 instance,如果程式運行中不需要使用這個 instance,那就變成無謂的浪費資源了。如果想要使用的時候再建立 instance 會改成以下寫法。
第二種:惰性初始化 – non thread safe
public sealed class LazySingleton
{
private static LazySingleton _instance = null;
private LazySingleton() { }
public static LazySingleton GetInstance()
{
if (_instance == null) //當null的時候才建立instance
_instance = new LazySingleton();
return _instance;
}
}
單執行緒的時候這樣的寫法沒問題,但多執行緒時可能都剛好遇到 instance = null 的情況,分別都建立 instance,就會取得不同的 instance。
為了避免這種情況,會再加上 lock
第三種:惰性初始化 – thread safe – lock(bad performance)
public sealed class LazySingleton
{
private static LazySingleton _instance = null;
private static readonly object _objLock = new object();
private LazySingleton() { }
public static LazySingleton GetInstance()
{
lock (_objLock)//確保一次只有一個thread可以進入
{
if (_instance == null)
{
_instance = new LazySingleton();
}
return _instance;
}
}
}
但是這種寫法,只要 GetInstance() 都會因為 lock 而一直在等待導致效能變差,實際上需要 lock 的只有 new LazySingleton() 而已,因此會再改成以下寫法,這種寫法大家稱為 double check locking
第四種:惰性初始化 – thread safe – double check locking
public sealed class LazySingleton
{
private static LazySingleton _instance = null;
private static readonly object _objLock = new object();
private LazySingleton()
{
}
public static LazySingleton GetInstance()
{
if (_instance == null)//first check
{
lock (_objLock)
{
if (_instance == null)//double check
{
_instance = new LazySingleton();
}
}
}
return _instance;
}
}
第五種:惰性初始化 – 使用 .Net 4 Lazy<T>
//thread safe
public sealed class LazySingleton
{
private static readonly Lazy<LazySingleton> _instance = new Lazy<LazySingleton>(() => new LazySingleton());
private LazySingleton() { }
public static LazySingleton Instance
{
get
{
return _instance.Value;
}
}
}
第六種:完全惰性初始化(fully lazy instantiation)
public sealed class LazySingleton
{
public static LazySingleton Instance { get { return LazySingletonNested.instance; } }
private LazySingleton() { }
private class LazySingletonNested
{
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static LazySingletonNested()
{
}
internal static readonly LazySingleton instance = new LazySingleton();
}
}