下面做一些简要的说明。

1.

单例模式(Singleton Pattern),又称作单件模式,当然也有一种诙谐的称谓:单身模式。在经典的GoF所著的《Design Patterns》一书中,对单例模式有着详尽的介绍,这本书网上有全文版本

2.

单例模式的意图是保证一个类仅有一个实例,并且要提供一个全局访问点来访问这个实例。通常这个全局访问点是一个静态方法或者C#中的一个属性。

3.

在C#中,典型的单例模式实现方法可以如下:

public class Manager
{
    private static Manager Mgr;
 
    //constructor must be private
    private Manager() { }
 
    //public and static, or you can change it to Property instead of Method.
    public static Manager GetInstance()
    {
        if (Mgr == null)
        {
            Mgr = new Manager();
        }
        return Mgr;
    }
}

当外部需要Manager的实例时,可以调用GetInstance()这个静态方法。由于Manager类的构造器是私有的,这也就避免了其他方式实例化这个Manager类。GetInstance()方法内部的实现,保证了全局中只有一个Manager实例。

4.

问题肯定不会这么简单就被解决,比如在多线程环境中,上述代码就会有很大的隐患。

  • 有一种情况很常见:两个线程同时调用GetInstance()方法;
  • 当某一个线程由于Mgrnull而进入条件判断代码块的时候,而恰恰还没有执行实例化一个Manager对象,这时候另一个线程由于Mgrnull,所以也会进入这个条件语句中

上面两种情况,很显然都会创建Manager实例,这也就违背了单例模式的意图了。

利用C#的特性,我们可以把一个线程先锁住(lock),等到这个线程完成后,再让下一个线程访问GetInstance()方法:

private static readonly object syncObject = new object();
 
//public and static, or you can change it to Property instead of Method.
public static Manager GetInstance()
{
    //double check
    if (Mgr == null)
    {
        lock (syncObject)
        {
            if (Mgr == null)
            {
                Mgr = new Manager();
            }
        }
    }
    return Mgr;
}

代码中用到了双重检查锁定(double check locking)的技术,是为了提高性能考虑,因为C#中lock语句是很耗性能的。第一道检查,是基于如果Mgr不为null的时候就不需要lock了,提高性能。第二道检查,是基于两个线程同时通过第一道检查后,第一个线程解锁后,由于Mgr此时已经不为null,所以第二个线程就不用实例化Manager了。

5.

单例模式有两种实现方式,主要基于构建的方式不同:

  1. 延迟初始化(Lazy Initialization),也叫“懒汉模式”:单例实例在第一次使用时被构建;
  2. 热初始化(Eager Initialization),也叫“恶汉模式”:单例实例在类加载时创建

前面创建单例模式的方式都属于延迟初始化。.NET 4.0以后提供了一个Lazy<T>泛型类,可以被应用于这个场景,省却代码的编写量。

public class Manager
{
    private static Lazy<Manager> mgr = new Lazy<Manager>();
     
    //version of Thread Safe
    //private static Lazy<Manager> mgr = new Lazy<Manager>(true);
     
    private Manager() { }
 
    public static Manager GetInstance()
    {
        return mgr.Value;
    }
}

Lazy<T>构造器重载版本可以帮我们解决多线程的问题。

C#使用静态初始化来完成单例模式中的热初始化。需要注意的是,不需要考虑多线程的问题,因为CLR会自动解决多线程同步的问题。如果程序经常要用到这个实例,运用热初始化可以显著提高性能。

public class Manager
{
    private static readonly Manager mgr = new Manager();
 
    private Manager() { }
 
    public static Manager GetInstance()
    {
        return mgr;
    }
}

6.

StackOverflow中对于单例模式都是持否定态度的,比如这个

In theory: when you need to restrict the instantiation of an object to one instance. In practice: never.

主要基于下面几个原因:

  • 违反了单一职责原则
  • 耦合度过大
  • 单元测试基本无法进行
  • 开发混淆,造成混乱。比如作为API提供的时候。

总之,

There‘s at most a can use but there no need.

文章来源:http://laobian.me/2013/02/singleton-pattern-with-csharp.html

单例模式以及在C#中的使用的更多相关文章

  1. 单例模式@Singleton在测试中的运用

    前言 单例模式是一种比较常用的设计模式,目的是:保证一个类仅有一个实例,并提供一个访问它的全局访问点. 测试种可能用到的场景 : 在很多时候,有些对象我们希望在整个程序只有一个实例,如线程池.数据库连 ...

  2. 单例模式读取properties配置文件中的信息

    public class ConfigManager {    private static ConfigManager config = null;    //创建Properties文件  读取配 ...

  3. Objective-C中的单例模式

    ​    ​单例模式算是设计模式中比较简单的一种吧,设计模式不是只针对某种编程语言,在C++, Java, PHP等其他OOP语言也有设计模式,笔者初接触设计模式是通过<漫谈设计模式>了解 ...

  4. Java中的GOF23(23中设计模式)--------- 单例模式(Singleton)

    Java中的GOF23(23中设计模式)--------- 单例模式(Singleton) 在Java这这门语言里面,它的优点在于它本身的可移植性上面,而要做到可移植的话,本身就需要一个中介作为翻译工 ...

  5. Android中的单例模式

    定义: 单例模式:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 使用场景: 确保某一个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源,或者某种类型的对象只应该有且只有一 ...

  6. java中的单例模式与doublecheck

    转自: http://devbean.blog.51cto.com/448512/203501 在GoF的23种设计模式中,单例模式是比较简单的一种.然而,有时候越是简单的东西越容易出现问题.下面就单 ...

  7. java中的单例模式与静态类

    单例模式与静态类(一个类,所有方法为静态方法)是另一个非常有趣的问题,在<Java中有关单例模式的面试问题>博文中露掉了,由于单例模式和静态类都具有良好的访问性,它们之间有许多相似之处,例 ...

  8. 设计模式 - Java中单例模式的6种写法及优缺点对比

    目录 1 为什么要用单例模式 1.1 什么是单例模式 1.2 单例模式的思路和优势 2 写法① - 饥饿模式 2.1 代码示例 2.2 优缺点比较 3 写法② - 懒惰模式 3.1 代码示例 3.2 ...

  9. 设计模式(五): 简单而又不失其重要性的单例模式(Singleton Pattern)

    上篇博客我们系统的介绍了三种工厂模式,今天我们就来介绍一下单例模式.单例模式虽然简单,但是还是比较重要的,是常用设计模式之一.在之前的博客<Objective-C中的单例模式>中介绍了Ob ...

随机推荐

  1. java 数组常见操作

    1.遍历  依次访问数组中的每个元素 public class ArrayDemo04 { public static void main(String[] args) { int[] arr = { ...

  2. Git中.gitignore忽略规则

    # 此为注释 – 将被 Git 忽略 *.a # 忽略所有 .a 结尾的文件 !lib.a # 但 lib.a 除外 /TODO # 仅仅忽略项目根目录下的 TODO 文件,不包括 subdir/TO ...

  3. Java 基础数据类型

    Java 提供的基础数据类型(也称内置数据类型)包含:整数类型.浮点类型.字符类型.布尔类型. 整数类型 整数类型变量用来表示整数的数据类型.整数类型又分为字节型(byte).短整型(short).整 ...

  4. C++——多态

    1. 多态定义的构成条件 多态是在不同继承关系的类对象,去调同一函数,产生了不同的行为. 就是说,有一对继承关系的两个类,这两个类里面都有一个函数且名字.参数.返回值均相同,然后我们通过调用函数来实现 ...

  5. csp-s模拟测试80(b)

    csp-s模拟测试80(b) 水题没什么可写的. $T1SB$规律题正解调了仨小时就过了. $T2SBDp$题颓完题解就秒了. $T3SB$数据结构考场想到正解就弃了,考后太懒一半正解一发随机化就A了 ...

  6. qfc 问题汇总(TO BE CONTINUED)

    硬件问题 增加一个复位按钮 程序问题 /* uart allocation: PB6-7: UART1 -> TELEM PD5-6 : UART2 -> SBUS PD8-9: UART ...

  7. day 50 MySQL数据备份与还原(mysqldump)

      MySQL数据备份与还原(mysqldump)   一 mysqldump指令实现数据备份.mysql指令实现数据还原 经常有朋友问我,DBA到底是做什么的,百科上说:数据库管理员(Databas ...

  8. IDEA 创建普通的maven+java Project

    最近想把以前积累的零散java练习和学习的东西建一个项目整理出来上传到码云托管,免得电脑挂了啥也找不到 配置是IDEA2017+java8+maven3.2.5,截图记录下步骤 第一步:File--& ...

  9. JavaScript实现几种常见的图形

    一.四种常见的三角形 第一种三角形: for(var i=1;i<=5;i++){        for( var j=i;j<=5;j++){                 docum ...

  10. springboot+vue的前后端分离与合并方案

    pringboot和vue结合的方案网络上的主要有以下两种: 1. [不推荐]在html中直接使用script标签引入vue和一些常用的组件,这种方式和以前传统的开发是一样的,只是可以很爽的使用vue ...