当前位置: 首页 > news >正文

C#上位机中的单例应用思考

文章目录

  • 一、前言
  • 二、上位机单例应用场景
    • 2.1 上位机
    • 2.2 单例及其应用
    • 2.3 上位机中的应用
      • 2.3.1 用户登录信息
      • 2.3.2 配置文件
      • 2.3.3 数据连接池
    • 2.4 一个应用场景的思考
  • 三、总结


一、前言

之前写过一篇关于单例的文——C#中单例模式的实现,讲了讲单例是什么以及在C#中的常见代码实现,那篇文的内容偏理论,并不实用。

最近在用WPF写上位机,发现我在实际开发中使用单例时,并不关心其底层实现,也不太会出现这样的单例类代码:

using System;public sealed class Singleton
{private static volatile Singleton instance;private static object syncRoot = new Object();private Singleton() {}public static Singleton Instance{get {if (instance == null) {lock (syncRoot) {if (instance == null) instance = new Singleton();}}return instance;}}
}

而往往是创建一个服务容器(ServiceProvider),然后把想要实现单例的类以单例模式加入其中,并将服务容器公开(通常是放在App类中)以使整个程序代码都能访问之,在想要用到该单例时,从容器中取出即可:

public IServiceProvider Services { get; }private static IServiceProvider ConfigureServices()
{var services = new ServiceCollection();services.AddSingleton<Class>();...return services.BuildServiceProvider();
}

如果你还没有用过这种容器的方式,可能会觉得很麻烦;而一旦接受了这种方式,你会发现它变成了一种定式。几乎所有应用程序都可以这么做(服务容器的这种方式本身也是一种设计模式Ioc)。

这些内容不是本文要讲的东西,本文主要想讲讲上位机程序中单例的应用,以及一个场景该怎样使用单例的思考。


二、上位机单例应用场景

2.1 上位机

先提一下上位机,
上位机通常不是什么庞大的程序,它主要用以:

  • 提供界面,用户可友好操作;
  • 与下位机通讯,将采集的数据加工,并呈现在界面上;
  • 将部分数据存储至数据库,以供报表、查询、统计分析;
  • 与更上层的系统(MES、ERP等)进行对接;
  • 可能还会结合一些专业技术(如视觉、文档处理等)辅助生产。

这样一个体量不大的用于专门设备的程序,其涉及的技术还是挺广的。

2.2 单例及其应用

单例的目的是为了保证一个类在程序中只有一个实例,并提供一个访问它的全局访问点。
很明显,单例这样的设计使一个类只有一个实例,并且要易于外界访问,从而方便对实例进行控制并节约系统资源。

因此,它的应用场景通常为:

  • 有频繁实例化(也就是频繁new)然后销毁的情况;
  • 创建对象耗时过多或者耗资源过多的情况;
  • 频繁访问IO资源的对象,如数据库连接池或文件。

我相信很多人第一次使用单例并不是因为性能的问题,而仅仅想要一个类似于C语言中全局变量的东西,希望有一个类的实例能被不同页面的代码访问到。这其实就是单例中提到的提供全局访问点的特性。

这边有一个大家非常熟悉的应用——Windows上的任务管理器。ctrl + shift + esc打开,并且无论你按多少次,都只会出现一个任务管理器。也就是说,在Windows系统这个程序中,任务管理器是唯一的。

那为什么这样设计,不这样设计会怎样呢?

  1. 如果弹出了多个任务管理器窗口,且这些窗口展示的内容完全一致,这样打开的就全是重复的对象了,就会造成系统资源的浪费,内存的损耗。实际使用中根本不需要多个呈现相同内容的窗口。
  2. 如果弹出了多个任务管理器窗口,且内容不一致,那就更糟糕了。这意味着,某一时刻应用的使用情况和进程、服务等信息存在多个状态,那到底哪个才是真实的呢?显然这更不可取。

由此可见,确保任务管理器在系统中有且仅有一个非常重要。

2.3 上位机中的应用

在上位机的开发中,也会经常遇到类似情况。下面举几个常见的例子:

2.3.1 用户登录信息

上位机有时需要权限功能,某些页面功能需要特定权限才能操作。
也就是在不同页面上,获取到的用户信息是一致的。要实现这个需求,用户信息就要全局唯一。往往是在用户登录时,将包含各种权限的用户信息加载到单例中。

2.3.2 配置文件

上位机程序中经常需要一些参数配置文件,比如设备相关的、用户习惯相关的。如果不使用单例,每次都要new对象,重新读一遍配置文件,很影响性能。如果使用单例,只需要开始时读一遍就好。

2.3.3 数据连接池

为什么要做池化?
因为新建连接很耗时,如果每次新的任务来了,都新建连接,那严重影响性能。所以一般做法是在一个应用中维护一个连接池,这样当任务来时,若有空闲连接,可以直接使用,省去了初始化的开销。

注意:
这里说的单例是对池做单例,而不是对单个数据库连接做单例。
如果是把一个数据库连接对象封存在单例对象中,这样是错误的。如果对单个数据库连接做单例,那多方请求连接时,就只能用一个数据库连接,那不是死的很惨?

2.4 一个应用场景的思考

除了以上的常规使用,我还尝试在页面切换时保留状态的需求上使用单例。
具体场景是这样的,在一个MVVM模式的上位机中有多个页面,我希望切换页面后再切回原页面(页面即Page,可以看成View),其呈现内容仍是之前的。页面的内容可以理解为ViewModel中的属性、命令等。

针对该需求,可以有多种方式使用单例,

  1. 将ViewModel中的部分关键对象单例化(通常是ViewModel中聚合的Model);
  2. 将ViewModel单例化,使程序中仅有一份ViewModel;
  3. 将整个View单例化。

方式1

若将ViewModel中的关键对象单例化,切换回原页面时就重新创建ViewModel,并在其中加载这些单例对象。

方式2

若将整个ViewModel单例化,仅需将View的DataContext绑定到单例ViewModel即可。

方式3

若将整个View单例化,切换页面只需要导航到目标单例View即可。

那么问题来了,哪种方式比较好呢?
这种问题显然没有答案,得看更具体的场景。
你甚至可以不用单例,在主页类中聚合几个子页面,然后点击导航到子页面就好。

现在回到使用单例的情况,
如果你viewmodel中聚合了不少model,并且model可能在其他页面也有使用,那显然对于这些model是应该做单例化的。

如果你viewmodel中有许多独立的状态项,只记录该页面的情况,和model几乎无关。那将整个ViewModel单例也是合理的。

如果你View中有一些联动的对象,比如Canvas,你在Canvas上画了一些画,而Canvas是属于View的。那将View做单例也很合理。

最终到底是用哪种方式,没有一个明确的答案。目前只能根据实际情况选取一种看似合理的方式,通过实践来检验。


三、总结

单例是很基础的设计模式,记住它是为了 保证一个类在程序中只有一个实例,并提供一个访问它的全局访问点 即可。

常见的应用场景,用户状态、配置文件、数据库连接池等。
在多页面用到同一个model时也可以使用。有些场景的使用上不必过于纠结,可达到效果即可。

相关文章:

C#上位机中的单例应用思考

文章目录 一、前言二、上位机单例应用场景2.1 上位机2.2 单例及其应用2.3 上位机中的应用2.3.1 用户登录信息2.3.2 配置文件2.3.3 数据连接池 2.4 一个应用场景的思考 三、总结 一、前言 之前写过一篇关于单例的文——C#中单例模式的实现&#xff0c;讲了讲单例是什么以及在C#…...

Python分享之redis

String 操作 redis中的String在在内存中按照一个name对应一个value来存储 set() #在Redis中设置值&#xff0c;默认不存在则创建&#xff0c;存在则修改 r.set(name, zhangsan) 参数&#xff1a; set(name, value, exNone, pxNone, nxFalse, xxFalse) ex&#xff…...

Linux常用命令——dd命令

在线Linux命令查询工具 dd 复制文件并对原文件的内容进行转换和格式化处理 补充说明 dd命令用于复制文件并对原文件的内容进行转换和格式化处理。dd命令功能很强大的&#xff0c;对于一些比较底层的问题&#xff0c;使用dd命令往往可以得到出人意料的效果。用的比较多的还是…...

DETR-《End-to-End Object Detection with Transformers》论文精读笔记

DETR&#xff08;基于Transformer架构的目标检测方法开山之作&#xff09; End-to-End Object Detection with Transformers 参考&#xff1a;跟着李沐学AI-DETR 论文精读【论文精读】 摘要 在摘要部分作者&#xff0c;主要说明了如下几点&#xff1a; DETR是一个端到端&am…...

网络流量监控-sniffnet

{alert type“info”} 今天来分享一个监控流量的应用sniffnet。 github项目地址&#xff1a;https://github.com/GyulyVGC/sniffnet {/alert} 可以在github的readme上看到这个程序有的特性&#xff1a; 为什么要介绍它呢&#xff1a;主要是多线程、跨平台、可靠、操作简单 我…...

验证go循环删除slice,map的操作和map delete操作不会释放底层内存的问题

目录 切片 for 循环删除切片元素其他循环中删除slice元素的方法方法1方法2&#xff08;推荐&#xff09;方法3 官方提供的方法结论 切片 for 循环删除map元素goalng map delete操作不会释放底层内存go map原理源码CRUD查询新增 操作注意事项map元素是无法取址的map是线程不安全…...

C++二级题2

数字字符求和 #include<iostream> #include<string.h> #include<stdio.h> #include<iomanip> #include<cmath> #include<bits/stdc.h> int a[2000][2000]; int b[2000]; char c[2000]; long long n; using namespace std; int main() {ci…...

DataWhale 机器学习夏令营第三期——任务二:可视化分析

DataWhale 机器学习夏令营第三期 学习记录二 (2023.08.23)——可视化分析1.赛题理解2. 数据可视化分析2.1 用户维度特征分布分析2.2 时间特征分布分析 DataWhale 机器学习夏令营第三期 ——用户新增预测挑战赛 学习记录二 (2023.08.23)——可视化分析 2023.08.17 已跑通baseli…...

ubuntu 上安装flutter dart android studio

因为国内网站不能使用 使用一下&#xff1a; vi ~/.bashrc 最后添加 export FLUTTER_STORAGE_BASE_URLhttps://mirrors.cloud.tencent.com/flutter export PUB_HOSTED_URLhttps://mirrors.tuna.tsinghua.edu.cn/dart-pub export PATH$PATH:/usr/local/go/bin export GOPROXY…...

【Golang】go交叉编译

交叉编译是用来在一个平台上生成另一个平台的可执行程序 。Go 命令集是原生支持交叉编译的。 Mac下编译&#xff1a;Linux 或 Windows 的可执行程序 # linux 可执行程序 CGO_ENABLED0 GOOSlinux GOARCHamd64 go build main.go # Windows可执行程序 CGO_ENABLED0 GOOSwindow…...

【人工智能】—_贝叶斯网络、概率图模型、全局语义、因果链、朴素贝叶斯模型、枚举推理、变量消元

文章目录 频率学派 vs. 贝叶斯学派贝叶斯学派Probability&#xff08;概率&#xff09;:独立性/条件独立性&#xff1a;Probability Theory&#xff08;概率论&#xff09;:Graphical models &#xff08;概率图模型&#xff09;什么是图模型&#xff08;Graphical Models&…...

学习笔记:ROS使用经验( 查看rostopic的信息)

查看topic的信息 要查看ROS中的话题信息&#xff0c;你可以使用以下命令&#xff1a; 1.查看所有活动话题&#xff1a; $ rostopic list这将列出当前运行的所有活动话题。 2.查看特定话题的消息类型&#xff1a; $ rostopic info <topic_name>将<topic_name>替…...

数据库——redis内存淘汰,持久化机制

文章目录 Redis 内存淘汰机制了解么&#xff1f;⭐了解操作系统中lru并尝试用java实现lru 2.Redis 持久化机制(怎么保证 Redis 挂掉之后再重启数据可以进行恢复)快照&#xff08;snapshotting&#xff09;持久化&#xff08;RDB&#xff09;AOF&#xff08;append-only file&am…...

亚马逊云科技 云技能孵化营 我也说ai

自从chatgpt大火以后&#xff0c;我也关注了人工智能方面的东西&#xff0c;偶尔同学推荐参加了亚马逊云科技云技能孵化营活动&#xff0c;免费学习了亚马逊云科技和机器学习方面的知识&#xff0c;还获得了小礼品&#xff0c;现在将活动及心得分享给大家。 活动内容&#xff…...

『PyQt5-基础篇』| 04 Qt Designer的初步快速了解

04 Qt Designer的初步快速了解 1 Qt Designer入口2 Qt Designer-Widget Box2.1 窗口部件盒(Widget Box)2.2 Layouts布局2.3 Spacers间隔部件2.4 Button按钮2.5 Item Views(Model-Based)2.6 Item Widgets(Item-Based)2.7 Containers容器2.8 Input Widget输入部件2.9 Display W…...

SpringCloud学习笔记(十一)_Hystrix仪表盘

我们来看一下如何使用它吧 1.引入依赖 1 2 3 4 5 6 7 8 9 10 11 12 | <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </depende…...

# ruby安装设置笔记

ruby安装设置笔记 文章目录 ruby安装设置笔记1 克隆并设置环境变量2 安装ruby3 设置ruby4 设置源5 安装bundler6 检查安装后的软件版本7 ubuntu 20.04 默认ruby环境 系统自带的ruby版本低了&#xff0c;需要手动安装更高版本&#xff08;使用rbenv方式&#xff09; 环境&#x…...

关于对文件路径权限判断的记录

首先需要添加引用 using System.Security.AccessControl;以下为具体代码&#xff0c;其中fileServerPath为需要判断的文件路径 #region Authority judgmentDirectorySecurity fileAcl Directory.GetAccessControl(fileServerPath);var rules fileAcl.GetAccessRules(true, t…...

git 基础

1.下载安装Git&#xff08;略&#xff09; 2.打开git bash窗口 3.查看版本号、设置用户名和邮箱 用户名和邮箱可以随意起&#xff0c;与GitHub的账号邮箱没有关系 4.初始化git 在D盘中新建gitspace文件夹&#xff0c;并在该目录下打开git bash窗口 git init 初始化完成后会…...

C语言网络编程实现广播

1.概念 如果同时发给局域网中的所有主机&#xff0c;称为广播 我们可以使用命令查看我们Linux下当前的广播地址&#xff1a;ifconfig 2.广播地址 以192.168.1.0 (255.255.255.0) 网段为例&#xff0c;最大的主机地址192.168.1.255代表该网段的广播地址&#xff08;具体以ifcon…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩

目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

基于Flask实现的医疗保险欺诈识别监测模型

基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施&#xff0c;由雇主和个人按一定比例缴纳保险费&#xff0c;建立社会医疗保险基金&#xff0c;支付雇员医疗费用的一种医疗保险制度&#xff0c; 它是促进社会文明和进步的…...

大数据零基础学习day1之环境准备和大数据初步理解

学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 &#xff08;1&#xff09;设置网关 打开VMware虚拟机&#xff0c;点击编辑…...

前端导出带有合并单元格的列表

// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解

本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)

一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解&#xff0c;适合用作学习或写简历项目背景说明。 &#x1f9e0; 一、概念简介&#xff1a;Solidity 合约开发 Solidity 是一种专门为 以太坊&#xff08;Ethereum&#xff09;平台编写智能合约的高级编…...

大模型多显卡多服务器并行计算方法与实践指南

一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...

C# 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

vulnyx Blogger writeup

信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面&#xff0c;gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress&#xff0c;说明目标所使用的cms是wordpress&#xff0c;访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...