【wif系列】C#之单例模式(Singleton Pattern)最佳实践
前言
在上一篇译文——《深入理解C#——在C#中实现单例模式》中,对在C#中实现单例模式进行了详细阐述。我们在日常的开发中可以采用解决方案4或解决方案6来实现单例模式,但每个单例类都需要单独实现。
我们再来看看使用单例模式的一些场景:
主要意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
主要解决:一个全局使用的类频繁地创建与销毁。
何时使用:当您想控制实例数目,节省系统资源的时候。
使用场景:
- 要求生产唯一序列号;
- WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来;
- 创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等;
- 全局配置文件访问,单例类来保证数据唯一性;
- 日志记录帮助类,为节省资源,全局一个实例一般就够了;
- 桌面应用常常要求只能打开一个程序实例或一个窗口。
单例基类
可以看到单例模式在程序开发中是非常常见的。既然我们会频繁的使用单例模式,那么有没有什么方式可以更方便的生产我们的单例。当然有,我们往下看。
对于没有基类的一些类的单例模式实现,可以考虑继承自单例基类。由单例基类派生的类必须是密封类,它确保您不能从这个单例类创建子类。单例的生产就由基类来完成,派生类只需要定义一个无参数的私有构造函数即可,它确保不能在外部创建此类的实例。通过调用继承的实例属性访问类的单例实例和公共成员。
/// <summary>
/// 总括来说,为了使用单例基类创建单例类,您需要执行以下操作:
///
/// 1) 定义一个派生自SingletonBase [T]的密封类,其中T是您定义的类名。 它确保您不能从此单例类创建子类。
/// 2) 在类中定义一个无参数的私有构造函数。它确保不能在外部创建此类的实例。
/// 3) 通过调用Instance属性来访问类的单例实例和公共成员。
///
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class SingletonBase<T> where T : class
{
#region Properties
/// <summary>
/// 获取该类的单例实例。
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")]
public static T Instance => SingletonFactory.Instance;
#endregion
#region Constructors
#endregion
/// <summary>
/// 创建单例实例的单例类工厂。
/// </summary>
private class SingletonFactory
{
#region Fields
/// <summary>
/// 定义弱引用实例。
/// </summary>
private static WeakReference _instance;
#endregion
#region Properties
/// <summary>
/// 获取实例。
/// </summary>
internal static T Instance
{
get
{
if (!(_instance?.Target is T comparer))
{
comparer = GetInstance();
_instance = new WeakReference(comparer);
}
return comparer;
}
}
#endregion
#region Constructors
/// <summary>
/// 防止编译器生成默认构造函数。
/// </summary>
private SingletonFactory()
{
}
/// <summary>
/// 显式静态构造函数,告诉c#编译器不要将类型标记为BeforeFieldInit。
/// </summary>
[SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")]
static SingletonFactory()
{
}
#endregion
#region Methods
/// <summary>
/// 获取特定类型的实例。
/// </summary>
/// <returns>The <see cref="T"/></returns>
[SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId =
"System.Type.InvokeMember")]
private static T GetInstance()
{
var theType = typeof(T);
T inst;
try
{
inst = (T) theType.InvokeMember(theType.Name,
BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.NonPublic,
null, null, null,
CultureInfo.InvariantCulture);
}
catch (MissingMethodException ex)
{
throw new TypeLoadException(
string.Format(CultureInfo.CurrentCulture,
"The type '{0}' must have a private constructor to be used in the Singleton pattern.",
theType.FullName), ex);
}
return inst;
}
#endregion
}
}
在SingletonBase中我们用到了WeakReference,它表示弱引用,即在引用对象的同时仍然允许通过垃圾回收来回收该对象。一般使用场景:对象过大,并且不经常访问。这样我们就可以创建一个弱引用,当不常用该对象的时候,GC可以回收该对象,当需要引用对象,可以先判断弱引用的对象是不是存在,如果存在,就直接使用,如果弱引用的对象已经被回收,那就重新创建一个对象来使用。对于单例对象来说,一般生命周期和应用程序域同步,是无法自动回收的。通过使用WeakReference来确保单例对象在长时间未使用时可以自动释放资源。
当然这里的WeakReference也可以替换为Lazy,根据是否需自动回收单例对象和子类是否包含属性和字段这两个方面来选择。如子类中包含属性和字段,则自动回收会导致属性和字段值重置,从而出现不可预估的后果。
单例提供者
对于有基类的类来说,上面的单例基类显然是不合适的。我们可以考虑实现一个单例提供者来生产我们的单例。
通过泛型类传参的方式实现如下:
/// <summary>
/// 用于从另一个类创建或获取单例的静态助手。
/// </summary>
/// <typeparam name="T">要创建或获取单例的类型。</typeparam>
public static class SingletonProvider<T> where T : class, new()
{
#region Fields
/// <summary>
/// 获取给定类型的单例。
/// </summary>
private static readonly Lazy<T> _lazy = new Lazy<T>(() => new T());
#endregion
#region Properties
/// <summary>
/// 获取给定类型的单例。
/// </summary>
public static T Instance => _lazy.Value;
#endregion
}
除了泛型类传参,还可以通过Get方法传参,实现如下:
/// <summary>
/// 用于从另一个类创建或获取单例的静态助手。
/// </summary>
public static class SingletonProvider
{
#region Methods
/// <summary>
/// 获取指定类型的单例。
/// </summary>
/// <typeparam name="TParameter">单例类型。</typeparam>
/// <returns>The <see cref="TParameter" />单例对象。</returns>
public static TParameter Get<TParameter>() where TParameter : class, new()
{
return SingletonProvider<TParameter>.Instance;
}
#endregion
}
总结
有了以上两种生产单例的方式,我们可以在开发中愉快的使用单例,而免除了具体的繁琐实现。
【wif系列】C#之单例模式(Singleton Pattern)最佳实践的更多相关文章
- 设计模式系列之单例模式(Singleton Pattern)——确保对象的唯一性
模式概述 模式定义 模式结构图 饿汉式单例与懒汉式单例 饿汉式单例 懒汉式单例 模式应用 模式在JDK中的应用 模式在开源项目中的应用 模式总结 主要优点 适用场景 说明:设计模式系列文章是读刘伟所著 ...
- 浅谈设计模式--单例模式(Singleton Pattern)
题外话:好久没写blog,做知识归纳整理了.本来设计模式就是个坑,各种文章也写烂了.不过,不是自己写的东西,缺少点知识的存在感.目前还没做到光看即能记住,得写.所以准备跳入设计模式这个大坑. 开篇先贡 ...
- 设计模式之单例模式(Singleton Pattern)
单例模式 单例模式(Singleton Pattern)在java中算是最常用的设计模式之一,主要用于控制控制类实例的数量,防止外部实例化或者修改.单例模式在某些场景下可以提高系统运行效率.实现中的主 ...
- 乐在其中设计模式(C#) - 单例模式(Singleton Pattern)
原文:乐在其中设计模式(C#) - 单例模式(Singleton Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 单例模式(Singleton Pattern) 作者:weba ...
- 【设计模式】单例模式 Singleton Pattern
通常我们在写程序的时候会碰到一个类只允许在整个系统中只存在一个实例(Instance) 的情况, 比如说我们想做一计数器,统计某些接口调用的次数,通常我们的数据库连接也是只期望有一个实例.Windo ...
- 抽象工厂(Abstract Factory),工厂方法(Factory Method),单例模式(Singleton Pattern)
在谈工厂之前,先阐述一个观点:那就是在实际程序设计中,为了设计灵活的多态代码,代码中尽量不使用new去实例化一个对象,那么不使用new去实例化对象,剩下可用的方法就可以选择使用工厂方法,原型复制等去实 ...
- 二十四种设计模式:单例模式(Singleton Pattern)
单例模式(Singleton Pattern) 介绍保证一个类仅有一个实例,并提供一个访问它的全局访问点. 示例保证一个类仅有一个实例. Singleton using System; using S ...
- 设计模式系列之单例模式(Singleton Pattern)
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式.这种模式涉及到一个单一的类,该类负责创建自己的对象 ...
- Java 基础:单例模式 Singleton Pattern
1.简介 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 这种模式涉及到一个单一的类,该类负责创 ...
- Net设计模式实例之单例模式( Singleton Pattern)
一.单例模式简介(Brief Introduction) 单例模式(Singleton Pattern),保证一个类只有一个实例,并提供一个访问它的全局访问点.单例模式因为Singleton封装它的唯 ...
随机推荐
- Netty实现高性能IOT服务器(Groza)之手撕MQTT协议篇上
前言 诞生及优势 MQTT由Andy Stanford-Clark(IBM)和Arlen Nipper(Eurotech,现为Cirrus Link)于1999年开发,用于监测穿越沙漠的石油管道.目标 ...
- 学习 javascript (一)javascript 简介
javascript 从一个简单的输入验证器发展成为一门强大的编程语言. 历史 以前我们输入一个表单,点击完提交后,服务器发送反馈给我们.比如填写姓名的时候,我们在前端不能限定人们只能输入汉字,需要服 ...
- Java - 静态代理详讲
Java - 静态代理详讲 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] 写在前面:*此章内容比较抽象,所以需要结合实际操作进行讲解* *需要有 ...
- Java面试前需要了解的东西
一.前言 只有光头才能变强 回顾前面: 广州三本找Java实习经历 上一篇写了自己面试的经历和一些在面试的时候遇到的题目(笔试题和面试题). 我在面试前针对Java基础也花了不少的时间,期间也将自己写 ...
- Smobiler Service是什么?(Smobiler——.NET移动开发平台)
在得知Smobiler即将推出新产品SmobilerService之后,许多人第一个疑问便是——Smobiler Service是什么? Smobiler的开发者对这个exe窗口一定不陌生,有时候因为 ...
- DSAPI DS密法
DS密法是DYLIKE本人研发的一种针对文本字符串的高强度加密方法,本加密方法的优点是同源不同密,同一个源文本每次加密的结果都不同,长度也不同.密钥最大可达String类型的字符最大长度.缺点是解密时 ...
- Mybatis插入数据返回主键ID
<insert id="add" parameterType="com.dsa.core.base.model.ProductSync"> ...
- MySQL 多实例的安装
Step 1 . 准备工作 关闭防火墙; 安装 MySQL依赖于libaio 库:下载,解压,重命名 MySQL的执行文件:新建用户组和用户 等准备工作在此不再赘述. 可执行文件目录为 /data/m ...
- 阿里云RDS for SQL Server测试吐槽
最近测试了一下阿里云RDS for SQL Server,有些设计简直就是反人类,让人不得不吐槽一番. 1:控制台创建数据库时,数据库名不能包含大小字母. 如上截图所示,数据库名称不能包含大写字母,好 ...
- Sqlserver 事务处理模板
USE StuDB GO /****** Object: StoredProcedure [dbo].[proc_live_send_answer_v4] Script Date: 06/20/201 ...