Singleton (单例) — 【面向对象设计模式学习】
Singleton (单例) — 【面向对象设计模式学习】
By CityWalker 2010年3月16日
Intent
a) Ensure a class has only one instance, and provide a global point of access to it.
b) Encapsulated “just-in-time initialization” or “initialization on first use”.
Scene
Application needs one, and only one, instance of an object. Additionally, lazy initialization and global access are necessary.
Structure
Implementations
There are various different ways of implementing the singleton pattern in C#. I shall present them here in reverse order of elegance, starting with the most commonly seen, which is not thread-safe, and working up to a fully lazily-loaded, thread-safe, simple and highly performant version.
a) First Version - not thread-safe
public sealed class Singleton { static Singleton instance = null; Singleton() { } public static Singleton Instance { get { if (instance == null) { instance = new Singleton(); } return instance; } } }
b) Second Version - simple thread-safety
public sealed class Singleton { static Singleton instance = null; static readonly object padlock = new object(); Singleton() { } public static Singleton Instance { get { lock (padlock) { if (instance == null) { instance = new Singleton(); } } return instance; } } }
c) Third Version - Double-check locking
public sealed class Singleton { static Singleton instance = null; static readonly object padlock = new object(); Singleton() { } public static Singleton Instance { get { if (instance == null) { lock (padlock) { if (instance == null) { instance = new Singleton(); } } } return instance; } } }
d) Fourth Version - not quite as lazy, but thread-safe without using locks
public sealed class Singleton { static readonly Singleton instance = new Singleton(); static Singleton() { } Singleton() { } public static Singleton Instance { get { return instance; } } }
Note: It doesn't work in java.
e) Fifth Version - fully lazy instantiation
public sealed class Singleton { public Singleton() { } public static Singleton Instance { get { return Nest.instance; } } } public class Nest { static Nest() { } internal static readonly Singleton instance = new Singleton(); }
Conclusion:
Singleton模式相对比较简单,有前辈们的总结,它的用法也就基本掌握了,就以上5种实现而言,第四种或第五种应该是比较适用的,无论从线程安全、性能、还是代码优雅方面都是不错的选择。对于.NET开发,第四种应该是首选,因为它很好的利用了.NET 平台的优势:“共语言运行库来初始化变量,解决了 Singleton 模式试图解决的两个基本问题:全局访问和实例化控制。公共静态属性为访问实例提供了一个全局访问点。此外,由于构造函数是私有的,因此不能在类本身以外实例化 Singleton 类;因此,变量引用的是可以在系统中存在的唯一的实例。”
最后,对于吕老师的 C#设计模式(7)-Singleton Pattern 中提到的“不要使用单例模式存取全局变量。这违背了单例模式的用意,最好放到对应类的静态成员中。”不太理解,为什么会违背单例的用意呢 ?记得曾经遇到个问题,正是用单例才很好地解决了那个问题。看到吕老师这篇文章的时候,我有点迷惑了,下面是曾经的问题描述:
向导式的数据导入工具,由4个窗体组成,各个窗体类中都会对一些公共的数据进行操作,由于公共的数据较多,而windows窗体间传递数据本来就不方便,于是想了个办法,利用Singleton模式构造一个辅助类,只允许实例化一个对象,用于存储这些公共数据。
/// <summary> /// 助手类:用于各个页面间的数据传递 /// </summary> public class ImportHelper { // 采用单例模式创建一个全局对象在各个页面间传递数据 private static ImportHelper helper; private static object lockobject = new object(); private ImportHelper() { } public static ImportHelper CreateHelper() { if (helper == null) { lock (lockobject) { if (helper == null) { helper = new ImportHelper(); } } } return helper; } public static void DestroyHelper() { helper = null; } #region 属性字段 private Dictionary<string, List<string>> _columns = new Dictionary<string, List<string>>(); private Dictionary<string, System.Data.DataTable> _excelData = new Dictionary<string, DataTable>(); private Dictionary<string, string> _tableMapping = new Dictionary<string, string>(); private Dictionary<string, Dictionary<string, string>> _columnsMapping = new Dictionary<string, Dictionary<string, string>>(); private Dictionary<string, string[]> _relationColumns = new Dictionary<string, string[]>(); private Dictionary<string, string> _idsDic = new Dictionary<string, string>(); private Dictionary<string, System.Data.DataTable> _tableData = new Dictionary<string, DataTable>(); private Dictionary<string, Dictionary<string, string>> _codeInfoValue = new Dictionary<string, Dictionary<string, string>>(); private Dictionary<string, System.Data.DataTable> _importData = new Dictionary<string, System.Data.DataTable>(); private Dictionary<string, System.Data.DataTable> _failedData = new Dictionary<string, System.Data.DataTable>(); private DataTable _errorDataTable = new DataTable(); private DataTable _wrongData = new DataTable(); private DataTable _sourceTable = new DataTable(); private DataTable _mappingTable = new DataTable(); private List<string> _rootEntity = new List<string>(); private List<string> _unUseColumns = new List<string>(); private string _excelPath = string.Empty; private string _mainSheet = string.Empty; private string _detailSheet = string.Empty; private string _mainEntity = string.Empty; private int _mainCount = 0; private int _detailCount = 0; private int _mainFailedCount = 0; private int _detailFailedCount = 0; private ImportType _importKind; //表列名集合:<文件名(不包含路径),文件中读取的列名集合> public Dictionary<string, List<string>> Columns { get { return _columns; } set { _columns = value; } } ......
这里便是利用Singleton模式“存取全局变量”,并很好地解决了windows窗体间大量数据传递的问题,好像也没有什么问题哦。