介绍

单例模式是软件工程学中最富盛名的设计模式之一。从本质上看,单例模式只允许被其自身实例化一次,且向外部提供了一个访问该实例的接口。通常来说,单例对象进行实例化时一般不带参数,因为如果不同的实例化请求传递的参数不同的话会导致问题的产生。(若多个请求都是传递的同样的参数的话,工厂模式更应该被考虑)

C#中实现单例有很多种方法,本文将按顺序介绍非线程安全、完全懒汉式、线程安全和低/高性能集中版本。

在所有的实现版本中,都有以下几个共同点:

  • 唯一的、私有的且无参的构造函数,这样不允许外部类进行实例化;

  • 类是密封的,尽管这不是强制的,但是严格来讲从上一点来看密封类能有助于JIT的优化;

  • 一个静态变量应该指向类的唯一实例;

  • 一个公共的静态变量用于获得这个类的唯一实例(如果需要,应该创建它);

需要注意的是,本文中所有的例子中都是用一个 public static Instance的变量来访问单例类实例,要将其转换成公共函数是很容易的,但是这样并不会带来效率和线程安全上的提升。

Version 1 - 非线程安全

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

该版本在多线程下是不安全的,会创建多个实例,请不要在生产环境中使用!

因为如果两个线程同时运行到if(instance==null)判断时,就会创建两个实例,这是违背单例模式的初衷的。实际上在后面那个线程进行判断是已经生成了一个实例,但是对于不同的线程来说除非进行了线程间的通信,否则它是不知道的。

Version 2 - 简单的线程安全

public sealed class Singleton2
{
private static Singleton2 instance = null;
private static readonly object obj = new object();
private Singleton2() { }
public Singleton2 Instance
{
get
{
lock (obj)
{
if (instance == null)
{
instance = new Singleton2();
}
return instance;
}
}
}
}

该版本是线程安全的。通过对一个过线程共享的对象进行加锁操作,保证了在同一时刻只有一个线程在执行lock{}里的代码。当第一个线程在进行instance判断或创建时,后续线程必须等待直到前一线程执行完毕,因此保证了只有第一个线程能够创建instance实例。

但不幸的是,因为每次对instance的请求都会进行lock操作,其性能是不佳的。

需要注意的是,这里使用了一个private static object变量进行锁定,这是因为当如果对一个外部类可以访问的对象进行锁定时会导致性能低下甚至死锁。因此通常来说为了保证线程安全,进行加锁的对象应该是private的。

Version 3 - Double-check locking的线程安全

public sealed class Singleton3
{
private static Singleton3 instance = null;
private static object obj = new object();
private Singleton3() { }
public static Singleton3 Instance
{
get
{
if (instance == null)
{
lock (obj)
{
if (instance == null)
{
instance = new Singleton3();
}
}
}
return instance;
}
}
}

该版本中试图去避免每次访问都进行加锁操作并实现线程安全。然后,这段代码对Java不起作用,因Java的内存模型不能保证在构造函数一定在其他对象引用instance之前完成。还有重要的一点,它不如后面的实现方式。

Version 4 - 不完全懒汉式,但不加锁的线程安全

public sealed class Singleton4
{
private static readonly Singleton4 instance = new Singleton4();
/// <summary>
/// 显式的静态构造函数用来告诉C#编译器在其内容实例化之前不要标记其类型
/// </summary>
static Singleton4() { }
private Singleton4() { }
public static Singleton4 Instance { get { return instance; } }
}

这个版本是的实现非常的简单,但是却又是线程安全的。C#的静态构造函数只有在当其类的实例被创建或者有静态成员被引用时执行,在整个应用程序域中只会被执行一次。使用当前方式明显比前面版本中进行额外的判断要快。

当然这个版本也存在一些瑕疵:

  • 不是真正意义上的懒汉模式(需要的时候才创建实例),若单例类还存在其他静态成员,当其他类第一次引用这些成员时便会创建该instance。下个版本实现会修正这个问题;

  • 只有.NET中才具有beforefieldinit特性,即懒汉式实现。且在.Net 1.1以前的编译器不支持,不过这个现在来看问题不大;

所有版本中,只有这里将instance设置成了readonly,这不仅保证了代码的高校且显得十分短小。

Version 5 - 完全懒汉实例化

public sealed class Singleton5
{
private Singleton5() { }
public static Singleton5 Instance { get { return Nested.instance; } }
private class Nested
{
static Nested() { }
internal static readonly Singleton5 instance = new Singleton5();
}
}

该版本看起来稍微复杂难懂,其实只是在写法上实现了上一版本的瑕疵,通过内嵌类的方式先实现了只有在真正应用Instance时才进行实例化。其性能表现与上一版本无异。

Version 6 - 使用.NET 4 Lazy type 特性

public sealed class Singleton6
{
private static readonly Lazy<Singleton6> lazy =
new Lazy<Singleton6>(()=> new Singleton6());
public static Singleton6 Instance { get { return lazy.Value; } }
private Singleton6() { }
}

如果你使用的是.NET 4或其以上版本,可以使用System.Lazy type来实现完全懒汉式。其代码看起来也很简洁且性能表现也很好。

性能 VS 懒汉式

一般情况下,我们并不需要实现完全懒汉式,除非你的构造初始化执行了某些费时的工作。因此一般的,我们使用显式的静态构造函数就能够适用。

本文翻译自Implementing the Singleton Pattern in C#

Exception

有时候在进行构造函数初始化时可能 会抛出异常,但这对整个应用程序来说不应该是致命的,所以可能的情况下,你应该自己处理这种异常情况。

总结

上述提供的几种实现方法中,一般情况下提倡使用Version 4,除非遇到有时早于单列类实例化时就引用了其他静态成员。这种情况下,Version 2一旦被考虑,虽然它看起来会因加锁耗时,但是其实运行起来并没有你想的那么慢,关键是你很容易写对它。显然Version 1你永远都不应该考虑,Version 3在与Version 5的对比下也是不在考虑范围之内的。

C#实现单例模式的6种方法的更多相关文章

  1. JAVA实现单例模式的四种方法和一些特点

    JAVA实现单例模式的四种方法和一些特点,需要的朋友可以参考一下     一.饿汉式单例类 复制代码 代码如下: public class Singleton  {      private Sing ...

  2. Android 创建单例模式的几种方法

    java模式之单例模式:单例模式确保一个类只有一个实例,自行提供这个实例并向整个系统提供这个实例.特点:1,一个类只能有一个实例2,自己创建这个实例3,整个系统都要使用这个实例 Singleton模式 ...

  3. Java实现单例模式的9种方法

    一. 什么是单例模式 因程序需要,有时我们只需要某个类同时保留一个对象,不希望有更多对象,此时,我们则应考虑单例模式的设计. 二. 单例模式的特点 1. 单例模式只能有一个实例. 2. 单例类必须创建 ...

  4. Python单例模式的四种方法

    在这之前,先了解super()和__new__()方法 super()方法: 返回一个父类或兄弟类类型的代理对象,让你能够调用一些从继承过来的方法. 它有两个典型作用: a. 在单继承的类层次结构中, ...

  5. python单例模式的几种实现方法

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

  6. java设计模式之单例模式以及实现的几种方法

    java设计模式以及实现的几种方法,看到比较好的博客文章,收藏起来供以后再次阅读.. 参见:http://www.cnblogs.com/garryfu/p/7976546.html

  7. java设计模式之单例模式(几种写法及比较)

    概念: Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建 ...

  8. Android传递数据5种方法

       Android开发中,在不同模块(如Activity)间经常会有各种各样的数据需要相互传递,我把常用的几种 方法都收集到了一起.它们各有利弊,有各自的应用场景. 我现在把它们集中到一个例子中展示 ...

  9. java单例模式的几种写法比较

    概念: Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建 ...

  10. 单例模式的两种实现方式对比:DCL (double check idiom)双重检查 和 lazy initialization holder class(静态内部类)

    首先这两种方式都是延迟初始化机制,就是当要用到的时候再去初始化. 但是Effective Java书中说过:除非绝对必要,否则就不要这么做. 1. DCL (double checked lockin ...

随机推荐

  1. IDEA使用——生成超文本格式的(HTML)JavaDoc

    概述 在大型项目中不免会有很多对外暴露的方法与接口,那我们就需要在类或者方法上面按照JavaDoc标准编写注释,就比如这样: 前面我们也提到过在Idea中可以将鼠标停留在方法名上,然后点击F2查看方法 ...

  2. 【工作记录】JDBC连接MySQL,跨时区调查CST转Asia/Shangha

    根据业务要求,不同的国家设置jvm参数,来确定当前时区. // -Duser.timezone=Asia/Kolkata 印度加尔各答 GMT+05:30 // -Duser.timezone=Asi ...

  3. .net版OCR紧随PP-OCRv3重磅发布

    .net版OCR地址PaddleOCRSharp PaddleOCRSharp是一个基于PaddleOCR的C++代码修改并封装的.NET的工具类库.包含文本识别.文本检测.基于文本检测结果的统计分析 ...

  4. itest work(爱测试) 开源一站式接口测试&敏捷测试工作站 9.0.3

    (一)itest work 简介 itest work (爱测试)  一站式工作站让测试变得简单.敏捷,"好用.好看,好敏捷" ,是itest wrok 追求的目标.itest w ...

  5. 使用vscode编辑c语言

    在 Visual Studio Code (VSCode) 中配置 C 语言环境 步骤指南: 一,前期准备(安装扩展,软件包) 安装 C/C++ 扩展 打开 VSCode. 点击左侧边栏的扩展按钮(或 ...

  6. C# 利用Autofac批量接口注入依赖【学习记录】

    背景: 本人在一位大佬的Colder框架中看到了这个接口注入,然后呢就想学习一下ioc思想与di设计模式.此写法给我的感觉就是 非常的 优雅 ,优雅永不过时.关于接口注入的概念和ioc和di具体是什么 ...

  7. ConfigurationManager 读取的不是最新值

    用 ConfigurationManager 修改配置后,ConfigurationManager 读取的不是最新值. 解决方法: 第一种: ConfigurationManager.RefreshS ...

  8. .NET5 ASP.NET CORE 发布到IIS 文件无法替换

    由于默认是:进程内托管.要在IIS里停止网站,才能替换文件. 建议解决方案是:进程外(out-of-process)托管 记事本修改项目的  .csproj 文件(或在VS上,选中web项目,右键-编 ...

  9. PI规划会,研发团队价值聚焦的一剂良方

    随着数字化建设如火如荼地推进,中大型企业的数字化建设团队规模也越来越大,团队规模的扩大一方面带来了更多产能与可能性,另一方面,不同的角色在不同的业务场景也带来了一些现实问题,例如: 作为CIO 或产品 ...

  10. MoneyPrinterPlus:AI自动短视频生成工具,详细使用教程

    MoneyPrinterPlus是一款使用AI大模型技术,一键批量生成各类短视频,自动批量混剪短视频,自动把视频发布到抖音,快手,小红书,视频号上的轻松赚钱工具. 之前有出过一期基本的介绍,但是后台收 ...