前言
这篇是个人总结C#设计模式系列文章的第一篇,本系列文章将由浅入深的研究C#开发中常用到的各种设计模式。欢迎大家批评指正。
本篇将从单例模式的基本概念开始,由浅入深探究C#中单例模式的几种不同的实现方法,并分析其中的区别和各自的适用范围(有点啰嗦,大侠请直接跳过前面)。
基本概念
单例模式属于创建型模式,其意图是保证一个类只有一个实例,并提供一个可以访问它的全局访问点。对于一些情况只有一个实例很重要,例如一个用户登陆了一个系统之后经常在不同模块之间点击,理论上所有模块应该共用同一个Login对象。而如何才能保证一个类只有一个实例并且很容易被访问呢?最简单的方法就是让类本身保存它的唯一实例,也就是单例模式。
1、基本实现(惰性加载、懒汉模式)
1 public sealed class Singleton 2 { 3 public static Singleton singletonInstance; 4 private Singleton() { } 5 public static Singleton getSingletonInstance() 6 { 7 if (singletonInstance == null) 8 { 9 singletonInstance = new Singleton(); 10 } 11 return singletonInstance; 12 } 13 }
这种实现方式最常见,对于适用于单线程的小应用程序,通过懒加载避免了在应用程序启动时创建了不必要的实例。缺点也很明显,这种实现不是线程安全的,在多线程环境下有可能会得到多个类的实例(多个线程同时访问singletonInstance == null时)。
2、双重锁定
要想实现线程安全也很简单,使用lock即可(类似于java中的synchronized),代码如下:
1 public sealed class Singleton 2 { 3 public static Singleton singletonInstance; 4 private static readonly object temp_lock = new object(); 5 private Singleton() { } 6 public static Singleton getSingletonInstance() 7 { 8 if (singletonInstance == null) 9 { 10 lock (temp_lock) 11 { 12 if (singletonInstance == null) 13 { 14 singletonInstance = new Singleton(); 15 } 16 } 17 } 18 return singletonInstance; 19 } 20 }
lock关键字可以保证代码块被完整运行,不会被其他线程打断。temp_lock是一个进程辅助对象,线程在进入时先对辅助对象加锁然后再检测对象是否被创建,这样就可以确保只有一个实例被创建。在第10行处才加锁,可以减少一部分开销,不必每次都加锁。这种实现解决保证了线程安全,也实现了延迟加载,
3、饿汉模式
这种方式实现起来最简单,最常用,而且也是线程安全的, 由于构造函数是私有的,不会在类的外部被创建,而Singleton 实例被私有静态成员变量引用,所以在类的getSingletonInstance方法被首次调用之前,不会发生实例化。
public sealed class Singleton { private static Singleton singletonInstance = new Singleton(); private Singleton() { } public static Singleton getSingletonInstance() { return singletonInstance; } }
这种方法的缺点就是不能在对象实例化之前做执行其他任务,类被加载时,CLR会自动实例化这个类,而不是在第一次调用GetInstance()后才实例化。
4、内部类
public sealed class Singleton { private Singleton() { } private static class SingletonHolder{ internal static readonly Singleton singletonTnstance = new Singleton(); } public static Singleton getSingletonInstance { get { return SingletonHolder.singletonTnstance; } } }
使用这种方法初始化工作由SingletonHolder的一个静态成员来实现,可以实现延迟初始化,尤其是当实例化instance很消耗资源时更具备优势,上述几种的其他优点这里也具备,是值得推荐的一种实现方式。
由于C#中的enum类型不支持方法,所以不能像java中那样通过enum实现单例模式。
总结:
其实单例模式的只要思想在于“如何控制用户使用new对一个类的构造器的任意调用”。另外上述实现方法中没有考虑对象销毁的管理,当然对于.NET来说,这方面一般不需要我们来考虑。
同时单例模式有以下个优点:
1、保持对象状态一致,阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。
2、可以作为对全局变量的一种改进方式,避免一些存储唯一实例的全局变量。
3、内存开销,相对于创建多个实例,减少内存开销。
(暂时整理这么多了,有其他问题再补充。)