前言

在上一篇译文——《深入理解C#——在C#中实现单例模式》中,对在C#中实现单例模式进行了详细阐述。我们在日常的开发中可以采用解决方案4或解决方案6来实现单例模式,但每个单例类都需要单独实现。

我们再来看看使用单例模式的一些场景:

主要意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

主要解决:一个全局使用的类频繁地创建与销毁。

何时使用:当您想控制实例数目,节省系统资源的时候。

使用场景:

  1. 要求生产唯一序列号;
  2. WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来;
  3. 创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等;
  4. 全局配置文件访问,单例类来保证数据唯一性;
  5. 日志记录帮助类,为节省资源,全局一个实例一般就够了;
  6. 桌面应用常常要求只能打开一个程序实例或一个窗口。

单例基类

可以看到单例模式在程序开发中是非常常见的。既然我们会频繁的使用单例模式,那么有没有什么方式可以更方便的生产我们的单例。当然有,我们往下看。

对于没有基类的一些类的单例模式实现,可以考虑继承自单例基类。由单例基类派生的类必须是密封类,它确保您不能从这个单例类创建子类。单例的生产就由基类来完成,派生类只需要定义一个无参数的私有构造函数即可,它确保不能在外部创建此类的实例。通过调用继承的实例属性访问类的单例实例和公共成员。

  1. /// <summary>
  2. /// 总括来说,为了使用单例基类创建单例类,您需要执行以下操作:
  3. ///
  4. /// 1) 定义一个派生自SingletonBase [T]的密封类,其中T是您定义的类名。 它确保您不能从此单例类创建子类。
  5. /// 2) 在类中定义一个无参数的私有构造函数。它确保不能在外部创建此类的实例。
  6. /// 3) 通过调用Instance属性来访问类的单例实例和公共成员。
  7. ///
  8. /// </summary>
  9. /// <typeparam name="T"></typeparam>
  10. public abstract class SingletonBase<T> where T : class
  11. {
  12. #region Properties
  13. /// <summary>
  14. /// 获取该类的单例实例。
  15. /// </summary>
  16. [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")]
  17. public static T Instance => SingletonFactory.Instance;
  18. #endregion
  19. #region Constructors
  20. #endregion
  21. /// <summary>
  22. /// 创建单例实例的单例类工厂。
  23. /// </summary>
  24. private class SingletonFactory
  25. {
  26. #region Fields
  27. /// <summary>
  28. /// 定义弱引用实例。
  29. /// </summary>
  30. private static WeakReference _instance;
  31. #endregion
  32. #region Properties
  33. /// <summary>
  34. /// 获取实例。
  35. /// </summary>
  36. internal static T Instance
  37. {
  38. get
  39. {
  40. if (!(_instance?.Target is T comparer))
  41. {
  42. comparer = GetInstance();
  43. _instance = new WeakReference(comparer);
  44. }
  45. return comparer;
  46. }
  47. }
  48. #endregion
  49. #region Constructors
  50. /// <summary>
  51. /// 防止编译器生成默认构造函数。
  52. /// </summary>
  53. private SingletonFactory()
  54. {
  55. }
  56. /// <summary>
  57. /// 显式静态构造函数,告诉c#编译器不要将类型标记为BeforeFieldInit。
  58. /// </summary>
  59. [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")]
  60. static SingletonFactory()
  61. {
  62. }
  63. #endregion
  64. #region Methods
  65. /// <summary>
  66. /// 获取特定类型的实例。
  67. /// </summary>
  68. /// <returns>The <see cref="T"/></returns>
  69. [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId =
  70. "System.Type.InvokeMember")]
  71. private static T GetInstance()
  72. {
  73. var theType = typeof(T);
  74. T inst;
  75. try
  76. {
  77. inst = (T) theType.InvokeMember(theType.Name,
  78. BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.NonPublic,
  79. null, null, null,
  80. CultureInfo.InvariantCulture);
  81. }
  82. catch (MissingMethodException ex)
  83. {
  84. throw new TypeLoadException(
  85. string.Format(CultureInfo.CurrentCulture,
  86. "The type '{0}' must have a private constructor to be used in the Singleton pattern.",
  87. theType.FullName), ex);
  88. }
  89. return inst;
  90. }
  91. #endregion
  92. }
  93. }

SingletonBase中我们用到了WeakReference,它表示弱引用,即在引用对象的同时仍然允许通过垃圾回收来回收该对象。一般使用场景:对象过大,并且不经常访问。这样我们就可以创建一个弱引用,当不常用该对象的时候,GC可以回收该对象,当需要引用对象,可以先判断弱引用的对象是不是存在,如果存在,就直接使用,如果弱引用的对象已经被回收,那就重新创建一个对象来使用。对于单例对象来说,一般生命周期和应用程序域同步,是无法自动回收的。通过使用WeakReference来确保单例对象在长时间未使用时可以自动释放资源。

当然这里的WeakReference也可以替换为Lazy,根据是否需自动回收单例对象和子类是否包含属性和字段这两个方面来选择。如子类中包含属性和字段,则自动回收会导致属性和字段值重置,从而出现不可预估的后果。

单例提供者

对于有基类的类来说,上面的单例基类显然是不合适的。我们可以考虑实现一个单例提供者来生产我们的单例。

通过泛型类传参的方式实现如下:

  1. /// <summary>
  2. /// 用于从另一个类创建或获取单例的静态助手。
  3. /// </summary>
  4. /// <typeparam name="T">要创建或获取单例的类型。</typeparam>
  5. public static class SingletonProvider<T> where T : class, new()
  6. {
  7. #region Fields
  8. /// <summary>
  9. /// 获取给定类型的单例。
  10. /// </summary>
  11. private static readonly Lazy<T> _lazy = new Lazy<T>(() => new T());
  12. #endregion
  13. #region Properties
  14. /// <summary>
  15. /// 获取给定类型的单例。
  16. /// </summary>
  17. public static T Instance => _lazy.Value;
  18. #endregion
  19. }

除了泛型类传参,还可以通过Get方法传参,实现如下:

  1. /// <summary>
  2. /// 用于从另一个类创建或获取单例的静态助手。
  3. /// </summary>
  4. public static class SingletonProvider
  5. {
  6. #region Methods
  7. /// <summary>
  8. /// 获取指定类型的单例。
  9. /// </summary>
  10. /// <typeparam name="TParameter">单例类型。</typeparam>
  11. /// <returns>The <see cref="TParameter" />单例对象。</returns>
  12. public static TParameter Get<TParameter>() where TParameter : class, new()
  13. {
  14. return SingletonProvider<TParameter>.Instance;
  15. }
  16. #endregion
  17. }

总结

有了以上两种生产单例的方式,我们可以在开发中愉快的使用单例,而免除了具体的繁琐实现。

wif 项目代码:https://github.com/LeoYang-Chuese/wif

【wif系列】C#之单例模式(Singleton Pattern)最佳实践的更多相关文章

  1. 设计模式系列之单例模式(Singleton Pattern)——确保对象的唯一性

    模式概述 模式定义 模式结构图 饿汉式单例与懒汉式单例 饿汉式单例 懒汉式单例 模式应用 模式在JDK中的应用 模式在开源项目中的应用 模式总结 主要优点 适用场景 说明:设计模式系列文章是读刘伟所著 ...

  2. 浅谈设计模式--单例模式(Singleton Pattern)

    题外话:好久没写blog,做知识归纳整理了.本来设计模式就是个坑,各种文章也写烂了.不过,不是自己写的东西,缺少点知识的存在感.目前还没做到光看即能记住,得写.所以准备跳入设计模式这个大坑. 开篇先贡 ...

  3. 设计模式之单例模式(Singleton Pattern)

    单例模式 单例模式(Singleton Pattern)在java中算是最常用的设计模式之一,主要用于控制控制类实例的数量,防止外部实例化或者修改.单例模式在某些场景下可以提高系统运行效率.实现中的主 ...

  4. 乐在其中设计模式(C#) - 单例模式(Singleton Pattern)

    原文:乐在其中设计模式(C#) - 单例模式(Singleton Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 单例模式(Singleton Pattern) 作者:weba ...

  5. 【设计模式】单例模式 Singleton Pattern

    通常我们在写程序的时候会碰到一个类只允许在整个系统中只存在一个实例(Instance)  的情况, 比如说我们想做一计数器,统计某些接口调用的次数,通常我们的数据库连接也是只期望有一个实例.Windo ...

  6. 抽象工厂(Abstract Factory),工厂方法(Factory Method),单例模式(Singleton Pattern)

    在谈工厂之前,先阐述一个观点:那就是在实际程序设计中,为了设计灵活的多态代码,代码中尽量不使用new去实例化一个对象,那么不使用new去实例化对象,剩下可用的方法就可以选择使用工厂方法,原型复制等去实 ...

  7. 二十四种设计模式:单例模式(Singleton Pattern)

    单例模式(Singleton Pattern) 介绍保证一个类仅有一个实例,并提供一个访问它的全局访问点. 示例保证一个类仅有一个实例. Singleton using System; using S ...

  8. 设计模式系列之单例模式(Singleton Pattern)

    单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式.这种模式涉及到一个单一的类,该类负责创建自己的对象 ...

  9. Java 基础:单例模式 Singleton Pattern

    1.简介 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 这种模式涉及到一个单一的类,该类负责创 ...

  10. Net设计模式实例之单例模式( Singleton Pattern)

    一.单例模式简介(Brief Introduction) 单例模式(Singleton Pattern),保证一个类只有一个实例,并提供一个访问它的全局访问点.单例模式因为Singleton封装它的唯 ...

随机推荐

  1. 华为手机无法使用USB调试的解决方案

    在Android开发中,一直在使用华为的荣耀8进行调试,但是突然某一次,发现USB调试无法使用了,且在其他的电脑上进行调试也不行. 后来经过查资料,总算解决了此问题,在这里进行一下解决方案的记录. 需 ...

  2. Django解决跨域问题

    原理:浏览器的同源策略,其实我们的请求发送过去了,服务器也进行响应了,就是浏览器把响应给阻止响应而已 第一种方法jsonp 因为浏览器不对 <script> 标签里面的src属性进行阻止, ...

  3. 1. CMake 系列 - 从零构建动态库和静态库

    目录 1. 文件目录结构 2. 库文件源代码 3. 编译生成库文件 1. 文件目录结构 首先创建如下目录结构: └── lib ├── build # ├── CMakeLists.txt └── s ...

  4. traefik 结合 docker-compose 的快速安装及使用

    traefik 介绍 traefik 是一个为了让部署微服务更加便捷而诞生的现代HTTP反向代理.负载均衡工具. 它支持多种后台 (Docker, Swarm, Kubernetes, Maratho ...

  5. 我爱Java系列之《JavaEE学习笔记day12》---【缓冲流、转换流、序列/反序列化流、打印流】

    [缓冲流.转换流.序列/反序列化流.打印流] 一.缓冲流 1.字节缓冲输出流 java.io.BufferedOutputStream extends OutputStream 高效字节输出流 写入文 ...

  6. window系统下如何查看so库的信息

    转载请标明出处,维权必究:https://www.cnblogs.com/tangZH/p/10458388.html  linux系统下能够直接用命令行查看so库的信息,但是window系统下咋办好 ...

  7. 「技巧」如何将Sketch改为深色模式

    之前Sketch只能根据mac系统的外观设置变更皮肤.在更新了版本54之后,可以脱离操作系统,在自己的偏好设置中更改外观了. 准备 Sketch 54 更多工具:whose.design 第一步:打开 ...

  8. Cocos Creator—如何给资源打MD5版本号

    Cocos Creator 是Cocos最新一代的游戏开发者工具,基于 Cocos2d-x,组件化,脚本化,数据驱动,跨平台发布.Cocos Creator的开发思路已经逐步跟Unity 3D靠拢,写 ...

  9. .NET Core 3.0-preview3 发布

    .NET Core 3.0 Preview 3已经发布,框架和ASP.NET Core有许多有趣的更新.这是最重要的更新列表. 下载地址 :https://aka.ms/netcore3downloa ...

  10. GitHub开源:升讯威 SQLite 增强组件 Sheng.SQLite.Plus

    Github:https://github.com/iccb1013/Sheng.SQLite.Plus Sheng.SQLite.Plus 是一个对直接使用 ADO.NET 方式操作 SQLite ...