- 某个类只能有一个实例;
- 它必须自行创建这个实例;
- 它必须自行向整个系统提供这个实例。
- 单例模式的类只提供私有的构造函数,
- 类定义中含有一个该类的静态私有对象,
- 该类提供了一个静态的公有的函数用于创建或获取它本身的静态私有对象。
优缺点
优点
缺点
写法1(只适用于单线程环境)
由于单例模式只有一个实例,即没有继承,所以一般将其设为sealed。
同时把构造方法设为私有,防止从外部创建,我们可以定义一个静态的实例,在需要的时候创建该实例。
代码如下:
namespace _01_单例模式
{
sealed class Singleton1
{
private static Singleton1 instance;
private static Singleton1 Instance
{
get
{
if (instance == null)
{
instance = new Singleton1();
}
return instance;
}
}
private Singleton1()
{
}
}
}
这种写法比较简单,只有在instance为空时才会创建实例,避免了重复创建。
缺点:该代码在单线程下可以正常工作,但在多线程环境下可能会出现问题。如果存在两个线程同时运行到if (instance == null)
并且instance还未创建,那么两个线程都会创建一个实例,这样就不再满足单例模式要求了。
写法2(可以在多线程中正常工作)
为了解决写法1的缺点,需要加上一个同步锁。写法如下:
namespace _01_单例模式
{
sealed class Singleton2
{
private static object syncObj = new object();
private static Singleton2 instance;
private static Singleton2 Instance
{
get
{
if (instance == null)
{
lock (syncObj)
{
if (instance == null)
{
instance = new Singleton2();
}
}
}
return instance;
}
}
private Singleton2()
{
}
}
}
在这个类里面创建了一个object专门用与互斥锁判断,同时用了两个if语句。第一个语句限制只有在instance为空时进行互斥判断,提高了效率。
而第二个if,假如有两个线程同时运行到了lock (syncObj)
,其中一个线程向下执行,创建instance。创建之后另一个线程继续运行,如果没有第二个if,第二个线程还会创建instance,不符合要求。
该类解决了多线程的问题,不过运用了两个if,依然比较复杂。
【推荐】写法3(利用静态构造方法)
其实C#中又一个方法能够确保只调用一次,即静态构造方法。所以可以利用这个特点实现单例模式。
代码如下:
namespace _01_单例模式
{
sealed class Singleton3
{
private static Singleton3 instance = new Singleton3();
public static Singleton3 Instance
{
get
{
return instance;
}
}
private Singleton3()
{
}
}
}
由于C#是在调用静态构造方法时初始化静态变量,在运行时能确保只调用一次静态构造方法,这样就能保证只初始化一次instance。
不过该方式依然有个小缺点:C#中的静态构造方法并非程序员控制,而是系统自动调用。假如Singleton3中有个静态方法,当我们第一次调用该静态方法时,instance就会被创建。但我们的本意希望调用Singleton3.Instance时创建。所以这个方式可能会过早的创建实例,降低内存使用效率。
【推荐】写法4(按需创建)
为了优化写法3,我们可以额外利用一个类。写法如下:
namespace _01_单例模式
{
sealed class Singleton4
{
public static Singleton4 Instance
{
get
{
return Nested.instance;
}
}
class Nested
{
internal static readonly Singleton4 instance = new Singleton4();
static Nested()
{
}
}
private Singleton4()
{
}
}
}
在Singleton4内部定义了一个私有类型Nested,当我们第一次调用Singleton4.Instance时,会自动调用Nested的静态构造方法创建instance。如果不调用Singleton4.Instance,就不会触发.Net运行时调用Nested,也不会创建实例,这样就做到了按需创建。
1 comment
:exclaim: 👿