🚀24个C#设计模式目录    🍺源码地址

概述

单例模式在计算机系统中很常见,他保证系统中一个类只能有一个存在的实例。比如文件系统,计时器、ID生成器等等。对于这些类来说,如果存在多个实例就会意味着内容有可能不一致,如果内容完全一致,那就意味着浪费系统资源,因此保证其只有一个实例是非常必要的。

单例模式:确保一个类只有一个实例,并提供一个全局访问点来访问这个唯一实例。

单例模式有三个要点:

  • 只能有一个实例
  • 必须自行创建这个实例
  • 必须向整个系统提供整个实例

使用频率:★★★★☆

结构与实现

为了达到上述三个目的,我们需要为将类的构造函数变成私有,并且提供一个访问唯一实例的方法,在创建唯一实例时要保证实例未被创建过。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Singleton
{
private static Singleton instance = null;

private Singleton()
{
// 私有构造函数
}

public static Singleton GetInstance()
{
if(instance == null)
instance = new Singleton();
return instance;
}
}

我们可以通过以下客户端调用代码来验证多次获取的实例是相同的

1
2
3
4
5
6
Singleton s1 = Singleton.GetInstance();
Singleton s2 = Singleton.GetInstance();
if(s1 == s2)
{
Console.WriteLine("s1 == s2");
}

但是这种写法是线程不安全的,如果在未初始化的时候,两个线程同时执行到

1
if(instance == null)

就会创建出两个不同的实例,无法保证唯一性。为了解决这个问题,可以采用饿汉式单例或者懒汉式单例。

饿汉式单例

饿汉式单例的实现思路非常简单,就是在类被加载时就立即创建唯一实例。

1
2
3
4
5
6
7
8
9
10
11
public class EagerSingleton
{
private static EagerSingleton instance = new EagerSingleton();

private EagerSingleton() { }

public static EagerSingleton GetInstance()
{
return instance;
}
}

这样在后续访问实例时都不会涉及到创建的动作,保证线程安全。

懒汉式单例

懒汉式单例采用延迟加载,在第一次使用时创建唯一实例,通过双重检查锁定来保证线程安全。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class LazySingleton
{
private static LazySingleton instance = null;
private static readonly object syncRoot = new object();
private LazySingleton() { }
public static LazySingleton GetInstance()
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
{
instance = new LazySingleton();
}
}
}
return instance;
}
}

在C#中lock关键字可以保证lock的对象被一个线程获取时,阻止其他线程获取lock,直至lock被释放。

在现实应用中,考虑启动时的速度更重要还是运行时的速度更重要来选择懒汉式或者饿汉式。

优缺点

优点:

  1. 确保了对唯一实例的受控访问
  2. 因在内存中最多只会存在一个实例,节省资源

缺点:

  1. 没有抽象层,扩展困难
  2. 单例类的职责过重,既包含创建对象的方法,又包含类本身的业务,违背了单一职责原则