【C#】 资源共享和实例管理:静态类,Lazy<T>单例模式,IOC容器Singleton我们该如何选

文章目录

  • 前言
  • 一、静态类
    • 1.1 静态类的特点
    • 1.2 静态类的使用
    • 1.3 静态类的缺点
  • 二、单例模式
    • 2.1 Lazy延迟初始化
    • 2.2 Lazy< T>单例模式的使用
    • 2.3 单例模式的特点
  • 三、IOC的Singleton
  • 总结


前言

编写程序的时候,常常能碰到当某些数据或方法需要被整个程序共享,且不需要多个独立副本的场景。比如说一个系统配置信息,字符串处理、数据格式化等工具类扩展方法。我们期许这类共享信息,工具类扩展方法能够开箱即用。常用的解决方案一般有:静态类,单例模式,IOC容器中的Singleton服务。

  • 比较直接的方法有通过静态类。静态属性存放共享信息,静态方法实现这类公用的共享方法。

  • 使用普通类的话,也可也通过结合 Lazy< T>实现单例模式,类加载时不创建实例,第一次用访问点属性时才创建,多次访问也不会创建多个实例,保证线程安全。

  • 在现代开发中,可以借用IOC容器实现资源共享。项目启动的时候向容器里注册服务,并且将其生命周期设置为singleton,容器第一次解析服务会创建实例并缓存,后续请求容器获取时直接返回缓存的实例,这样也是全局唯一。

本文作为一个总结,记录并对比对这类资源共享和实例管理时采取的解决方案,希望能帮助到大家。


一、静态类

1.1 静态类的特点

静态类作为一种特殊的类类型,定义的时候通过static关键词修饰。本身是不能够被不能实例化,无法通过new关键字创建实例对象。静态类不能被继承,也不能实现接口。比起普通类,失去了多态的能力。

静态类只能包含只能包含静态成员,一个类被static修饰为静态类,其内部不能出现非静态字段,非静态方法,非静态属性。但是普通类里是可以包含静态字段、静态方法或者静态属性的。

静态类的生命周期和它所在的AppDomain生命周期一致,在程序加载类的时候被分配到静态存储区里(普通类的静态成员也是被分配到静态存储),全局会调用这个唯一对象。

这种全局共享,生命周期与程序一致的特性,并且简洁的调用方式使其天然容易实现信息共享,和工具扩展方法。

1.2 静态类的使用

比如这个系统信息配置类,记录App名称和版本,在AppDomain内,全局会调用这个唯一对象。
系统信息配置类

public static class AppConfig
{public static string AppName = "我的应用";public static string Version = "1.0.0";
}

系统信息配置信息的调用

var appName = AppConfig.AppName;

比方说接下来的这个静态工具方法,它要实现的是基于Newtonsoft相关的json序列化和反序列化。通过在静态类JsonTransfer下面添加静态扩展方法,其第一个参数指定调用该静态扩展方法参数的类型,用this指定。

这样在使用中,就能通过指定类型的对象后面直接调用定义的扩展方法十分的方便。

基于Newtonsoft的工具方法

public static class JsonTransfer
{/// <summary>/// 对象 => JSON字符串/// </summary>/// <param name="obj">对象</param>/// <returns></returns>public static string ToJSON(this object obj, List<JsonSerializedConfig> jsonSerializedConfigs){if (jsonSerializedConfigs.Contains(JsonSerializedConfig.CustomFormatDateTime)){return obj == null ? null : JsonConvert.SerializeObject(obj, Formatting.Indented);}else{return null;}}
}

工具扩展方法调用

WebUser webUser = new WebUser()
{UserID = 1,LoginName = "admin",
};
string? webUserStr = webUser.ToJSON();

1.3 静态类的缺点

但是静态成员不支持多态,静态类无法继承,也无法实现接口,扩展性很差。

二、单例模式

单例模式的核心是一个类仅仅有唯一实例,并且通过一个pulic的实例属性这个全局访问点来供外部函数调用, 它的出现解决了是普通类的对象如何实现仅实例化一次。这一点看上去于静态类相似,但是单例模式不拘泥于静态类,可以使普通类也能拥有一个唯一的实例,这点与静态类截然不同。

2.1 Lazy延迟初始化

前面我们了解到单例模式使普通类也能仅存在一个实例,但是考虑到程序运行的性能更倾向于需要这个类的实例的时候才去实例化这个类,而且在多线程环境下,两个线程同时请求这个类的实例,如果不加以锁处理,多线程访问可能创建多个实例。

这里我们使用.NET提供的延迟初始化的泛型类型Lazy< T>。使用Lazy< T>能将类的实例化延迟到首次需要使用这个对象实例的时候,而不是在声明这个对象的时候就创建,节省资源,节省不必要的加载时间,提供程序运行性能。

Lazy< T>是通过其Value属性访问对象,仅在首次访问Value的时候,才会实例化对象。 而且Lazy< T> 内置了线程安全支持,多线程同时访问 Value 时,只有一个线程会执行初始化,其他线程阻塞等待,最终所有线程获取同一个实例。

2.2 Lazy< T>单例模式的使用

使用Lazy< T>能将类的实例化延迟到首次需要使用这个对象实例的时候,我们可以将一些比如数据库服务类相关的天然是单例类型的对象,使用Lazy< T>延迟加载,这里拿一个Redis服务类举例。

我们通过一个Lazy< RedisServer>类型的私有静态只读字段存储唯一实例,并且该RedisServer的构造函数私有,仅允许创建Lazy< T>实例的时候调用。

并且通过一个静态实例讲lazy.Value暴露出去,作为一个访问点,调用该类实例的各种方法。

Redis服务类

/// <summary>
/// Redis单例服务
/// </summary>
public sealed class RedisServer
{//私有静态字段,存储唯一实例private static readonly Lazy<RedisServer> lazy = new Lazy<RedisServer>(() => new RedisServer());/// <summary>/// 静态实例/// </summary>public static RedisServer Instance { get { return lazy.Value; } }/// <summary>/// Redis连接对象/// </summary>private readonly ConnectionMultiplexer _redis;/// <summary>/// Redis数据库对象/// </summary>private readonly IDatabase _db;/// <summary>/// Redis键前缀/// </summary>private readonly string _keyPrefix = "AlarmCenter_";/// <summary>/// Redis单例服务/// </summary>public RedisServer(){try{ConfigurationOptions config = new ConfigurationOptions(){EndPoints = { "127.0.0.1:6379" }, // Redis服务器地址和端口Password = "eastf@R123",          // Redis密码AbortOnConnectFail = false,       // 连接失败时不终止ConnectTimeout = 5000,            // 连接超时时间(毫秒)SyncTimeout = 5000                // 同步操作超时时间(毫秒)};// 创建Redis连接_redis = ConnectionMultiplexer.Connect(config);if (_redis.IsConnected){// 获取默认数据库(0)_db = _redis.GetDatabase();}else{Logger.Trace("无法连接到Redis服务器");throw new Exception("无法连接到Redis服务器");}}catch (Exception ex){// 处理连接异常Logger.Trace($"Redis连接错误: {ex.Message}");throw;}}/// <summary>/// 获取Redis数据库对象/// </summary>public IDatabase GetDatabase(){return _db;}/// <summary>/// 获取Redis键前缀/// </summary>/// <returns></returns>public string GetKeyPrefix(){return _keyPrefix;}public string GetKey(string key){return $"{_keyPrefix}{key}";}/// <summary>/// 关闭Redis连接/// </summary>public void CloseConnection(){_redis?.Close();_redis?.Dispose();}
}

2.3 单例模式的特点

由于单例模式的对象类型并不局限于静态类,它可以通过接口实现扩展,相比静态类更加的灵活。

三、IOC的Singleton

在现代的软件开发框架中,和IOC容器打交道真是家常便饭,其服务的注入分三种生命周期,分别是:Singleton,Socpe,Transient。

往IOC容器里注册一个Singleton生命周期的服务时,在首次解析到该服务的时候,会创建一个这个服务的实例,并且缓存下来。后续所有对该服务的请求都会返回这个缓存,并且会随着容器的销毁自动实现IDisposable,一并被GC回收。

比如我们还是编写一个Redis服务类,这次不需要编写额外的单例相关的代码。该服务类不需要关心类的实例化,线程安全,性能开销。
Redis服务类

services.AddSingleton<IRedisService, RedisService>();

总结

  • 简单工具类/配置,这些可以考虑静态类,编写和使用起来很简洁性。
  • 复杂的创建逻辑,并且要考虑到扩展,可以选择 Lazy< T>单例模式,兼顾灵活性与性能。
  • .NET Core开发环境下首选IOC Singleton,符合面向接口编程和低耦合原则。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/96270.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/96270.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL——存储引擎、索引

一、存储引擎1.MySQL体系结构2.存储引擎简介存储引擎就是储存数据、建立索引、更新/查询数据等技术的实现方式。储存引擎是基于表的&#xff0c;而不是基于库的&#xff0c;所以存储引擎也可被称为表类型建表语句&#xff1a;查询数据库支持的储存引擎&#xff1a;show engines…

机器学习01——机器学习概述

上一章&#xff1a;机器学习核心知识点目录 下一章&#xff1a;机器学习02——模型评估与选择 机器学习实战项目&#xff1a;【从 0 到 1 落地】机器学习实操项目目录&#xff1a;覆盖入门到进阶&#xff0c;大学生就业 / 竞赛必备 文章目录一、参考书推荐二、机器学习的基本概…

Shell编程:检测主机ip所在网段内其他在线ip

一、逻辑设计获取本机 ip 及 网段循环检测网段内所有 ip判断 ping 结果&#xff0c;符合条件的输出相关信息二、代码展示#!/bin/bash#获取本机ip local_iphostname -I #local_ipip addr| grep "inet "|grep -v 127.0.0.1| awk {print $2}#获取本机网段 networkecho $…

Windows安装Chroma DB

安装步骤 安装python 3.8或以上的版本创建虚拟环境&#xff1a;python -m venv chroma_env激活虚拟环境&#xff1a;.\chroma_env\Scripts\activate安装Chroma DB&#xff1a;pip install chromadb(可选)安装扩展功能&#xff1a;pip install sentence-transformers pypdf tikt…

李彦宏亲自说

昨天&#xff0c;李彦宏亲自说&#xff1a;百度的数字人直播以假乱真&#xff0c;很多人是看不出这是数字人&#xff0c;而且转化率很高”这几个月百度一直在推“数字人”不再强调“大模型”了。数字人是AI落地最适合企业的一款产品&#xff0c;一般用于客服、面试、直播带货等…

JS 中bind、call、apply的区别以及手写bind

1.作用call、apply、bind作用是改变函数执行的上下文&#xff0c;简而言之就是改变函数运行时的this指向那么什么情况下需要改变this的指向呢&#xff1f;下面举个例子var name "lucy"; var obj {name: "martin",say: function () {console.log(this.nam…

vue2(7)-单页应用程序路由

1.单页应用程序如 单页&#xff1a;网易云&#xff0c;多页&#xff1a;京东单页应用程序&#xff0c;之所以开发效率高&#xff0c;性能高&#xff0c;用户体验好最大的原因是&#xff1a;页面按需更新 要按需更新&#xff0c;就要明确访问路径和组件的关系这时候就要用…

vue中通过heatmap.js实现热力图(多个热力点)热区展示(带鼠标移入弹窗)

直接上完整代码&#xff01;记录实现方式 注意heatmap.min.js需要通过heatmap.js提供的下载地址进行下载&#xff0c;地址放在下边 url&#xff1a;heatmap GIT地址 <template><div class"heatmap-view" ref"heatmapContainer"></div&g…

配置Kronos:k线金融大模型

github地址 网页btc预测demo使用的Kronos-mini模型 huggingface的仓库 文章目录配置环境安装python环境获取市场数据的库通过webui使用example中的例子prediction_example.py补充说明根据原例优化的代码CryptoDataFetcher单币对多周期预测配置环境 使用conda的环境. 首先进行换…

【Deep Learning】Ubuntu配置深度学习环境

【start: 250715】 文章目录ubuntu与深度学习安装cuda查看显卡信息&#xff08;nvidia-smi&#xff09;升级驱动下载cuda安装conda安装anaconda默认指向自己的conda初始化conda确认 conda.sh 被加载安装cuda-toolkit直接安装cuda-toolkit&#xff08;高级的&#xff09;安装高于…

车载数据采集(DAQ)解析

<摘要> 车载数据采集&#xff08;DAQ&#xff09;软件模块是现代汽车电子系统的核心组件&#xff0c;负责实时采集、处理、记录和传输车辆运行数据。本文系统解析了DAQ模块的开发&#xff0c;涵盖其随着汽车智能化演进的历史背景&#xff0c;深入阐释了信号、协议、缓存等…

强化学习框架Verl运行在单块Tesla P40 GPU配置策略及避坑指南

1.前言 由于比较穷,身边只有1块10年前的Tesla P40 GPU卡(2016年9月发布),想利用起来学习强化学习框架Verl。程序员学习开源代码,大部分人的第一直觉不是分析模块组成,而是跑起来试试,然后去debug一下后面的运行逻辑。 由于在官方部署指导文档中并未指明跑通Verl的最低…

leetcode169.多数元素

题目描述给定一个大小为 n 的数组 nums &#xff0c;返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。你可以假设数组是非空的&#xff0c;并且给定的数组总是存在多数元素。题目解法博耶-摩尔多数投票算法&#xff08;英语&#xff1a;Boyer–Moore…

基于机器学习的P2P网贷平台信用违约预测模型

使用平台提供的借款人信息&#xff08;年龄、收入、历史信用等&#xff09;和借款信息&#xff0c;构建一个二分类模型来预测借款人是否会违约。重点解决类别不平衡问题和模型可解释性。逻辑回归、随机森林、XGBoost、SMOTE过采样、模型评估&#xff08;AUC, KS, F1-Score&…

豆瓣网影视数据分析与应用

源码链接&#xff1a;点击下载源码 相关文档&#xff1a;点击下载相关文档 摘 要 随着互联网的快速发展&#xff0c;豆瓣网作为一个综合性的影视评分和评论平台&#xff0c;积累了大量的用户数据&#xff0c;这些数据为影视分析提供了丰富的素材。借助Hadoop这一大数据处理框…

四、计算机网络与分布式系统(中)

一、局域网与广域网1、局域网&#xff08;1&#xff09;定义将有限地理范围内的多台计算机通过传输媒体连接&#xff0c;借助网络软件实现设备间通信与资源共享的通信网络&#xff08;2&#xff09;特点1.地理范围小&#xff1a;通常为数百米至数公里内。2.传输速率高&#xff…

Python 面向对象实战:私有属性与公有属性的最佳实践——用线段类举例

描述 在绘图软件、GIS、CAD 或简单的图形编辑器中&#xff0c;线段&#xff08;Segment&#xff09;是非常基础的对象。每个线段有两个端点&#xff08;x1,y1&#xff09;和&#xff08;x2,y2&#xff09;。在实现时我们通常希望&#xff1a; 封装端点数据&#xff08;防止外部…

流式细胞术样本处理全攻略(一):组织、血液、体液制备方法详解

摘要 流式细胞术作为多参数、高通量的细胞分析技术,在细胞表型鉴定、免疫反应研究、疾病机制探索及药物效果评估中发挥关键作用。而样本制备是流式实验成功的核心前提,需将不同来源样本处理为单颗粒悬液,并最大程度减少细胞死亡与碎片干扰。本文针对组织、外周血 / 骨髓、体…

【C#】理解.NET内存机制:堆、栈与装箱拆箱的底层逻辑及优化技巧

文章目录前言一、栈与堆1.1 栈&#xff08;Stack&#xff09;1.1.1 基本信息1.1.2 特点1.2 堆&#xff08;Heap&#xff09;1.2.1 基本信息1.2.2 特点1.3 从代码中窥见堆栈二、装箱与拆箱2.1 装箱2.2 拆箱2.3 如何避免不必要的装箱与拆箱2.3.1 泛型集合2.3.2 泛型参数总结前言 …

人工智能学习:Transformer结构中的子层连接(Sublayer Connection)

Transformer结构中的子层连接(Sublayer Connection) 一、子层连接介绍 概念 子层连接(Sublayer Connection),也称为残差连接(Residual Connection),是Transformer模型中的一个关键设计,用于将多个子层(如自注意力层和前馈全连接层)组合在一起。它通过残差连…