C#设计模式 之 单例模式

739 阅读4分钟

本文已参与掘金创作者训练营第三期「高产更文」赛道,详情查看:掘力计划|创作者训练营第三期正在进行,「写」出个人影响力。

别名:单件模式、Singleton

一,意图

  单例模式是一种创建型设计模式, 让你能够保证一个类只有一个实例, 并提供一个访问该实例的全局节点。


二,动机

  在程序开发中,经常有这个样一些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确性以及良好的效率。

问题来了:   我们怎么才能保证一个类只有一个实例并且这个实例被容易的访问呢?

  也许你一下就想到:定义一个全局变量使得这个实例被访问, 并且我保证在这个程序中我只实例化一次这个对象。 当然,我相信作为一个程序员你是可以做到这一点的。

请看下这句话:这应该是类的设计者的责任,而不是使用者的责任。

这就引出了单例设计模式:   让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问改实例的方法。


三,结构

单例模式

  1. 单例 (Singleton) 类声明了一个名为 get­Instance获取实例的静态方法来返回其所属类的一个相同实例。
  2. 单例的构造函数必须对客户端 (Client) 代码隐藏。 调用 获取实例方法必须是获取单例对象的唯一方式。

四,优缺点

优点:

  • 可以派生:在单例类的实例构造器中可以设置以允许子类派生。
  • 受控访问:因为单例类封装他的唯一实例,所以它可以严格的控制其他程序怎样以及何时访问它。
  • 你获得了一个指向该实例的全局访问节点。
  • 仅在首次请求单例对象时对其进行初始化。

缺点:

  • 违反了单一职责原则。
  • 单例模式一般不要支持序列化,因为这也有可能导致多个对象实例。
  • 多线程环境下需要进行特殊处理, 避免多个线程多次创建单例对象。

五,应用场景

单例模式适合应用场景   如果程序中的某个类对于所有客户端只有一个可用的实例, 可以使用单例模式。

  单例模式禁止通过除特殊构建方法以外的任何方式来创建自身类的对象。 该方法可以创建一个新对象, 但如果该对象已经被创建, 则返回已有的对象。

   如果你需要更加严格地控制全局变量, 可以使用单例模式。

   单例模式与全局变量不同, 它保证类只存在一个实例。 除了单例类自己以外, 无法通过任何方式替换缓存的实例。

  • PS:你可以随时调整限制并设定生成单例实例的数量, 只需修改获取实例方法即可。

六,代码实现

实现方式:

  1. 在类中添加一个私有静态成员变量用于保存单例实例。
  2. 声明一个公有静态构建方法(属性)用于获取单例实例。
  3. 在静态方法(属性)中实现"延迟初始化"。 该方法会在首次被调用时创建一个新对象, 并将其存储在静态成员变量中。 此后该方法每次被调用时都返回该实例。
  4. 将类的构造函数设为私有。 类的静态方法仍能调用构造函数, 但是其他对象不能调用。

示例代码:

class Singleton
{
    private static Singleton _instance; //1.

    public static Singleton Instance //2.3.
    {
        get
        {
            if (_instance == null)
            {
                _instance = new Singleton();
            }
            return _instance;
         }
    }
    
    private Singleton() { } // 4.
}

多线程单例

当使用多线程访问上述代码时,可能会导致被创建多个单例,修改代码如下,即可实现多线程下的单例:

思路:通过加锁保证同一时间只有一个线程访问构造

 class SingletonThread
 {
    private static volatile SingletonThread _instance;

    private static object lockHelper = new Object { };

    public static SingletonThread Instance
    {
        get
        {
            // 双重校验,为避免额外的性能消耗
            if(_instance== null)
            {
            	// 当第一个线程运行到这里时,此时会对lock加锁,
            	// 当第二个线程运行该方法时,首先检测到lock加锁状态,该线程就会挂起等待第一个线程解锁
                lock (lockHelper)
                {
                	 if(_instance== null)
                	 {
                   		_instance= new SingletonThread();
                   	 }
                }
            }
            return _instance;
        }
    }

    // 私有化构造
    private SingletonThread() { }
}

volatile 关键字简介:传送门


另一种写法,利用静态构造器和只读属来制实现,(缺点:支持参数化构造)

.NET机制可控制静态构造器可以保证只有一个线程被执行

class SingletonRead {

   // 只要访问就会被执行静态构造器,不使用不会进行实例化
   public static readonly SingletonRead Instance = new Singleton_Read();

    // 等价于
    //public static readonly SingletonRead Instance;
    //static SingletonRead ()
    //{
    //    Instance = new SingletonRead ();
    //}
        
    private Singleton_Read(){}
}

设计模式系列博文示例代码工程:链接