设计模式之单例模式
所谓单例(Singleton)就是在应用程序运行期间,某个类型对外公布的实例始终是同一个,同一个的意思并不是说相等的,而是相同的,我们可以利用object的一个静态方法object.ReferenceEquals(而非object.Equals)来测试单例。
1、首先,该Singleton的构造函数必须是私有的,以保证客户程序不会通过new()操作产生一个实例,达到实现单例的目的。
2、其次,尽可能的把单例的类用sealed关键字修饰,防止类被继承。
2、因为静态变量的生命周期跟整个应用程序的生命周期是一样的,所以可以定义一个私有的静态全局变量instance来保存该类的唯一实例;
3、必须提供一个全局函数或者静态属性访问获得该实例,并且在该函数或者属性内部提供控制实例数量的功能,即通过if语句判断instance是否已被实例化,如果没有则用new()创建一个实例;否则,直接向客户返回一个实例。
测试代码
class Program { static void Main(string[] args) { Singleton instance1 = Singleton.Instance; Singleton instance2 = Singleton.Instance; bool result = object.ReferenceEquals(instance1, instance2); Console.WriteLine(result); Console.ReadKey(); } }
版本一
public sealed class Singleton { private static Singleton _instance; private Singleton() { } public static Singleton Instance { get { if(_instance==null) { _instance = new Singleton(); } return _instance; } } }
说明:这个版本中,没有考虑线程并发获取实例问题,即可能出现两个线程同时获取instance实例,且此时其为null时,就会出现两个线程分别创建了instance,违反了单例规则。
版本二
public sealed class Singleton { private static Singleton _instance; private static object obj = new object(); private Singleton() { } public static Singleton Instance { get { if(_instance==null) { lock (obj) { if (_instance == null) { _instance = new Singleton(); } } } return _instance; } } }
说明:这个和版本一唯一的区别就是保证了线程安全,使用了双重锁方式较好地解决了多线程下的单例模式实现。先看内层的if语句块,使用这个语句块时,先进行加锁操作,保证只有一个线程可以访问该语句块,进而保证只创建了一个实例。再看外层的if语句块,这使得每个线程欲获取实例时不必每次都得加锁,因为只有实例为空时(即需要创建一个实例),才需加锁创建,若果已存在一个实例,就直接返回该实例,节省了性能开销.(本人认为不需要加入volatile关键字修饰)。
版本三
public sealed class Singleton { private readonly static Singleton _instance = new Singleton(); private Singleton() { } public Singleton Instance { get { return _instance; } } }
说明:这个版本保证了在第一次使用时Singleton已经初始化了一个实例对象,比较简单方便,比较常用,不过有一点值得我们关注,那就是和版本四的比较,显示实现静态构造函数。
版本四
public sealed class Singleton { private static readonly Singleton _instance = new Singleton(); private Singleton() { } static Singleton() { } public Singleton Instance { get { return _instance; } } }
说明:这个版本和版本三基本一样,多了一个静态的构造函数,目的是为了消除IL代码中的beforefieldinit标记,在C#中,如果显示实现了静态构造函数则,则静态构造函数的执行是严格按照需要来执行的,即在第一次调用静态成员之前执行,如果没有显示实现静态构造函数,则静态构造函数的执行时机是随意的(只要保证在静态成员之前的任何时候都可以)。
详细的请参考Artech的关于Type Initializer和 BeforeFieldInit的问题,看看大家能否给出正确的解释 和 涛哥的.Net类型构造器
版本五
public sealed class Singleton { private static readonly Lazy<Singleton> _instance = new Lazy<Singleton>(()=>new Singleton()); // private static readonly Lazy<Singleton> _instance = new Lazy<Singleton>(()=>new Singleton(),System.Threading.LazyThreadSafetyMode.ExecutionAndPublication); private Singleton() { } static Singleton() { } public Singleton Instance { get { return _instance.Value; } } }
说明:这个版本实用了Lazy,个人感觉和版本四效果一样,都可以启到延迟加载的效果,上面注释掉的代码是设置线程安全的(由于Lazy本就是线程安全的,所以不设置也是一样的)。
版本六(网上搜的)
public abstract class Singleton<T> where T:class { private static readonly Lazy<T> _instance = new Lazy<T>(() => { var ctors = typeof(T).GetConstructors( BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); if (ctors.Count() != 1) throw new InvalidOperationException(String.Format("Type {0} must have exactly one constructor.", typeof(T))); var ctor = ctors.SingleOrDefault(c => c.GetParameters().Count() == 0 && c.IsPrivate); if (ctor == null) throw new InvalidOperationException(String.Format("The constructor for {0} must be private and take no parameters.", typeof(T))); return (T)ctor.Invoke(null); }); public static T Instance { get { return _instance.Value; } } }
说明:这是一个单例的基类,用abstract修饰,保证所有需要实用单例的类要继承它,实用反射创建类型对象,也不用担心效率问题,因为只执行一次,好处是写一个基类之后,其它需要实现单例的类就不要写太多代码(其实也不多哦)去实现单例了,但是注意私有构造函数还是必须的。
结束语
对于单例,有很多的实现方式,上面是几种简单的实现模式,个人认为没必须要搞的太复杂,只需要简单实用即可,希望对你有所帮助。