文章目录

  • 一、引言
  • 二、单例模式
    • 1. 单例模式
      • 1.1. 场景
      • 1.2. 问题产生
      • 1.3. 应当考虑的
      • 1.4. 解决方案
    • 2. C#中实现单例模式
      • 2.1. 场景
      • 2.2. 实现策略
        • 2.2.1. 单例(Singleton)
        • 2.2.2. 静态初始化
        • 2.2.3. 多线程单例
      • 2.3. 导致的一些问题
        • 2.3.1. 好处
        • 2.3.2. 存在的问题
      • 2.4. 鸣谢
  • 三、结尾语

一、引言

算是第一篇和设计模式正式相关的文。
相信写代码的都听说过设计模式,即使没有特意去学过。
而在这些设计模式之中,有一种很基础,也很常用的模式,就是单例模式(Singleton)。
那这篇文就来学习学习它。

二、单例模式

1. 单例模式

1.1. 场景

在一些场景下,在程序中需要一个特定的类,并且该类的数据需要能被所有其它对象访问。而在大多数情况下,系统中该类的数据也是唯一的。例如,界面上只能有一个鼠标指针,并且该鼠标指针能被所有程序访问。同样的还有,企业解决方案可以与管理到特定系统连接的单网关对象进行对接。

1.2. 问题产生

那么怎么去实现这样一个全局可用的对象实例,并且保证它只有一个实例被创建呢?

注意:这里对单例(singleton)的定义有意地比《设计模式:可重用面向对象软件的元素》[Gamma95]中的更窄。

1.3. 应当考虑的

在该场景下,当你考虑这个问题的解决方案时,必须注意并协调以下几点:
许多编程语言(如,VB 6.0或C++)都支持全局范围内的对象定义。这些对象都驻留在命名空间(namespace)的根上,对程序中所有对象而言都是普遍可用的。这种方法为全局可访问性的问题提供了一个简单的解决方案,但没有解决单实例的需求。因为它不会阻止其他对象创建全局对象的其他实例。此外,其他面向对象语言,如VB .NET或C#,并不直接支持全局变量(所以也无法直接用这种方案)。

为了确保一个类只能存在一个实例,必须得控制实例化过程。这意味着你需要通过使用编程语言中固有的实例化机制(例如,使用new操作符)来防止其它对象创建类的实例。控制实例化的另一部分是提供一种中心机制,通过这种机制,所有对象都可以获得对单个实例的引用。

这段话也说明了设计模式的一些特点,它不是与编程语言强相关的,它不是一个函数,一个类,而是更接近一种实现机制、实现思路。你通过任何语言都可以去实现它。

1.4. 解决方案

单例模式通过以下几方面来提供一个全局且单一(唯一)的实例:

  • 让类创建单个实例。
  • 允许其他对象通过返回实例引用的类方法来访问该实例。这个类方法是全局可访问的。
  • 将类构造函数声明为private,为的是其他对象不能创建新实例。

下图展示了该模式的静态结构。该UML类图非常简单,因为Singleton由一个简单的类组成,该类持有对自身单个实例的引用。

该图显示了Singleton类包含一个public的static属性,该属性返回对Singleton类的单个实例的引用。(静态类图中,+表示public,下划线表示static)。右上角的1表示系统中任何时候都只能有一个该类的实例。因为Singleton的默认构造函数是私有的,系统中的任何其他对象都必须通过Instance属性访问Singleton对象。

Singleton模式通常被归类为习惯用法而不是模式,因为解决方案主要取决于你所使用的编程语言的特性(例如,类方法和静态初始化器)。就如这个模式所做的那样,将抽象概念从特定的实现中分离出来,可能会使Singleton的实现看起来非常简单。

2. C#中实现单例模式

2.1. 场景

现在,你构建了一个C#程序。你需要一个仅有一个实例的类,并且需要一个该实例的全局访问点(global point of access)。你想确保你的方案是高效的,并且它能利用了微软.NET CLR的特性。你可能还希望你的方案是线程安全的。

2.2. 实现策略

尽管单例模式是一种相对简单的模式,但它也有许多不同的实现,你需要根据实现的不同,做一些权衡与选择。下面介绍了一系列的实现策略,并讨论了它们的优缺点。

2.2.1. 单例(Singleton)

该单例设计模式的实现遵循了设计模式中提出的解决方案:
可重用的面向对象软件元素[Gamma95],修改它以使得C#中语言特性可用,例如属性的修改:

下面提到的设计模式,有时候是指一本书或一套准则。
至于[Gamma95]以及相似格式的标识符应该是这本书中提到的一种解决方案。

using System;public class Singleton
{private static Singleton instance;private Singleton() {}public static Singleton Instance{get {if (instance == null){instance = new Singleton();}return instance;}}
}

该实现有两个主要优点:
因为实例是在instance属性方法中创建的,所以类可以执行额外的功能(例如,实例化一个子类),即使它可能引入一些不受欢迎的依赖项。

直到对象请求实例时才执行实例化;这样的方法称为懒汉实例化(lazy instantiation,也叫延迟、惰性实例化)。懒汉实例化避免了在程序启动时,实例化不必要的单例。

然而,这种实现主要缺点是它在多线程环境下是不安全的(即不是线程安全的)。如果独立的执行线程同时进入Instance属性方法,那么可能会创建多个Singleton对象的实例。每个线程都可以执行下面语句,并创建一个新的实例:

if (instance == null)

有许多方法可以解决这个问题。其中一种是使用叫双重检查锁(double-check locking)的惯用法。然而,C#结合CLR(公共语言运行库)提供了一种静态初始化(static initialization)方法,它可以避免这些问题,而不需要开发者显式地编写线程安全代码。

事实上,假如没有研究过单例模式,我觉得这种方法已经很不错了。
public提供属性的外部访问。
static保证只有一份。
私有构造函数又使外界不能实例化它。
唯一缺点正如上面所说,它不是线程安全的。因为if(instance==null)这条语句不是原子的。
许多简单的场景下,我觉得这种写法完全够用。

2.2.2. 静态初始化

《设计模式[Gamma95]》避免静态初始化的原因之一是(说明该书中还是抵制这种静态初始化的方法的),C++规范在静态变量的初始化顺序上留下了一些模糊性。幸运的是,.NET框架通过对变量初始化的处理解决了这种模糊性。

public sealed class Singleton
{private static readonly Singleton instance = new Singleton();private Singleton(){}public static Singleton Instance{get {return instance; }}
}

在这种策略中,当类中任何成员第一次被引用时,实例会被创建。CLR负责变量初始化。该类被标记为sealed以防止派生,因为派生可能会添加实例。有关将类标记为sealed的优缺点的讨论,参考[Sells03]。此外,变量被标记为readonly,这意味着只能在静态初始化期间(此处所显示的)或在类构造函数中对其赋值。

这种实现与前面示例相似,不同之处在于它依赖于CLR来初始化变量。它仍然处理了Singleton模式试图解决的两个基本问题:全局访问(global access)和初始化控制(initialization control)。这个公有静态属性(public static property)提供了实例的全局访问点。同时因为构造函数是私有的(private),Singleton类无法在类之外被实例化;因此,该变量就是系统中唯一存在的实例。

因为Singleton实例是由私有静态成员变量引用的,所以直到对Instance属性的调用第一次引用该类时,实例化才会发生。因此,该解决方案是延迟实例化属性的一种实现形式,正如《单例的设计模式形式》那样。

这种方法唯一的潜在缺点是那你对实例化机制的控制较少。在《设计模式形式》中介绍到,你能够在实例化之前使用非默认的构造函数或执行其他任务。因为.NET框架在这种解决方案中执行了初始化,所以你无法进行这些选项。在大多数情况下,静态初始化是.NET中实现一个单例的首选方法

2.2.3. 多线程单例

静态初始化对于大多数情况是可行的。当程序必须延迟实例化、使用非默认构造函数或在实例化之前执行其他任务,和在多线程环境中工作时,你就需要不同的解决方案了。但是,也存在着不能依赖CLR来确保线程安全的情况,例如在静态初始化示例中。在这种情况下,你必须使用特定的语言功能来确保在多线程环境下只创建一个实例。一种比较创建的解决方案是使用双重检查锁(Double-Check Locking)[Lea99]来隔离线程,避免同时创建单例的新实例。

注意:CLR解决了在其他环境中常见的与使用Double-Check Locking相关的问题。但也存在锁被打破的情况,相关更多信息,可以查看该网址

下面实现只允许一个线程进入临界区(critical area),这是锁块标识的,在还没有创建Singleton的实例时:

using System;public sealed class Singleton
{private static volatile Singleton instance;private static object syncRoot = new Object();private Singleton() {}public static Singleton Instance{get {if (instance == null) {lock (syncRoot) {if (instance == null) instance = new Singleton();}}return instance;}}
}

这种方法确保只创建一个实例,并且只在需要实例时创建。另外,将变量声明为volatile,以确保在访问实例变量之前完成对实例变量的赋值。最后,这种方法使用syncRoot实例来上锁,而不是锁住类型本身,以避免死锁。

这种双重检查锁的方法解决了线程并发性问题,同时避免了在每次调用Instance属性方法时使用独占锁。它还运行你延迟实例化,直到对象第一次被访问。事实上,程序很少使用这种实现。在大多数情况下,静态初始化方法就足矣。

2.3. 导致的一些问题

在C#中实现单例有以下好处和问题:

2.3.1. 好处

静态初始化方法是可行的,因为.NET框架显式地定义了静态变量初始化的方式和时间。

在“多线程单例”中描述的双重检查锁用法在CLR中得到了正确的实现。

2.3.2. 存在的问题

如果你的多线程程序需要显式初始化,则必须采取预防措施以避免线程问题。

2.4. 鸣谢

  • [Gamma95] Gamma, Helm, Johnson, and Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, 1995.

  • [Lea99] Lea, Doug. Concurrent Programming in Java, Second Edition. Addison-Wesley, 1999.

  • [Sells03] Sells, Chris. “Sealed Sucks.”

三、结尾语

虽然是个基础的单例模式,但涉及的知识并不简单。
其中明显就涉及了平时业务代码中不太会遇到的操作系统(Operating System)这门课的并发相关知识。尤其是多线程单例中,锁的示例,如果OS零基础或对并发没个概念还真难啃下来。
不过若只是应用单例的话,就如文中所说,理解静态初始化那段代码,然后把你的类套进去(即把静态初始化的Singleton类名换成你自己的类)就行了。

C#中单例模式的实现相关推荐

  1. PHP中单例模式:三私一公是什么?

    PHP中单例模式:三私一公是什么? 三私一公 私有化静态属性 私有化构造方法 私有化克隆方法 公有化静态方法 ​​​​ 转载于:https://www.cnblogs.com/phpisfirst/p ...

  2. rethat安装MySQL多例_Spring框架-Bean作用域中单例模式和多例模式的区别

    Spring框架-Bean作用域中单例模式和多例模式的区别 一.单例模式的特点(当没有指定是单例模式还是多例模式的时候,默认是单例模式): 1.Spring容器创建的时候,对应的类的实例化对象一起被创 ...

  3. python实现单例模式的几种方式_基于Python中单例模式的几种实现方式及优化详解...

    单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场. ...

  4. Unity+C#开发笔记(六)| unity中单例模式的使用 | ╭(●`∀´●)╯╰(●’◡’●)╮

    首先单例模式是做什么用的呢?对于我一个萌新来说,目前我用到的就是方便别的类调用!,所以在不继承MonoBehavior的类,我都喜欢加个单例模式方便调用(我知道好像正确原因不是这个0.0但主要是妹看懂 ...

  5. java 的方法是静态的类_Java中单例模式和静态方法类的区别

    最近翻看了一些资料,发现JAVA的单例模式并不简单:PHP并没有线程安全的问题,一个请求在结束后生命周期就结束了,PHP设计单例模式仅仅是为了如果在同一个页面多次处理,可以不用重复创建对象而已:JAV ...

  6. java中单例的应用_浅谈Java中单例模式的几种应用

    目录 浅谈Java中单例模式的几种应用 第一种:懒汉式 第二种:饿汉式 第三种:双重检索式 第四种:注册登记式 第五种:内部类形式 浅谈Java中单例模式的几种应用 日常开发中,为了提高我们系统中对象 ...

  7. python什么是单例模式_Python中单例模式是什么

    Python中单例模式是什么 发布时间:2020-07-20 14:14:20 来源:亿速云 阅读:61 作者:清晨 这篇文章将为大家详细讲解有关Python中单例模式是什么,小编觉得挺实用的,因此分 ...

  8. android 单例的作用,Android中单例模式的几个坑

    先来看这样一个单例,稍微有点经验的同学可能都会说,这样的单例是非线程安全的.要加个volatile关键字才可以.class Singleton{        private static  Sing ...

  9. IOS开发中单例模式使用详解

    第一.基本概念 单例模式是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例类的特殊类.通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问. 第二.在IOS中使用单例模式的情 ...

  10. python中单例模式是什么_python中的单例模式

    单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场. 比如,某 ...

最新文章

  1. 封装了一下我佛山人4.0 (支持vs2005)asp.net 页面验证
  2. 手把手教你用seq2seq模型创建数据产品(附代码)
  3. cve-2017-12629 apache solr xxe rce 漏洞分析
  4. Sql server管理工具SQLManagementStudio2008的安装
  5. (4) ebj学习:ejb发布web service
  6. 【Day12】整个前端性能提升大致分几类
  7. 微软 Small Basic 简体中文版 已经发布了
  8. VS2008无法识别的版本3.5
  9. LBP及纹理表达 转自http://blog.sina.com.cn/s/blog_ba9d7d9901018k4v.html
  10. paip.c++ sqlite数据库操作总结
  11. “茴”字有几种写法? Java 实现 WebSocket 的方式
  12. a标签下载图片 text
  13. 使用scrapy爬取豆瓣上面《战狼2》影评
  14. JavaScript---网络编程(9-2)--DHTML技术演示(2-2)-表格加强
  15. 关于新光源建设的一些想法
  16. 基于单片机的贪吃蛇设计
  17. 等级保护测评—Windows
  18. EM2040D和SES2000采集图像判读(一)
  19. document server java_Readme.md · ct_java/DocumentServer - Gitee.com
  20. 15w4k58s4引脚图_《51单片机轻松入门—基于STC15W4K系列》连载

热门文章

  1. 类人猿x64位封包协议拦截技术开发3种工具(支持安卓)
  2. MySQL酒店管理系统课程设计_酒店管理系统的设计与实现(PHP,MySQL)(含录像)
  3. 观察者模式C#实现实例(二)
  4. JAVA微信公众号开发第11微信发红包
  5. glsl boom
  6. setContentView时候报错
  7. java助教面试自我介绍_助教面试自我介绍参考
  8. 如何使用手机里的Windows系统云桌面?
  9. 服务器上的 Git - 在服务器上部署 Git
  10. TV-Android基本架构