C#:单例,闭包,委托与事件,线程,Parallel,Params,扩展方法,接口与抽象类

单例模式

在对泛型的约束中,最常使用的关键字有where 和 new。
其中where关键字是约束所使用的泛型,该泛型必须是where后面的类,或者继承自该类。
new()说明所使用的泛型,必须具有无参构造函数,这是为了能够正确的初始化对象

   /// <summary>/// C#单例模式/// </summary>public abstract class Singleton<T> where T : class,new(){private static T instance;private static object syncRoot = new Object();public static T Instance{get{if (instance == null){lock (syncRoot){if (instance == null)instance = new T();}}return instance;}}protected Singleton(){Init();}public virtual void Init() { }}

1.泛型约束class Singleton where T : class,new()
2.静态对象没创建,使用new T()
3.在构造函数中可以加入虚方法
在上述示例中,我们使用泛型类型参数 T 来表示子类。where T : class, new() 约束了 T 必须是一个引用类型并且必须有一个无参构造函数。instance 变量和 Instance 属性与之前的示例相同。
当你需要扩展该单例类时,你只需创建一个继承自 Singleton 的子类,并在其中实现你的逻辑:

public class MySingleton : Singleton<MySingleton>
{// your code here
}

闭包陷阱

闭包是一个代码块(在C#中,指的是匿名方法或者Lambda表达式,也就是匿名函数),并且这个代码块使用到了代码块以外的变量,于是这个代码块和用到的代码块以外的变量(上下文)被“封闭地包在一起”。当使用此代码块时,该代码块里使用的外部变量的值,是使用该代码块时的值,并不一定是创建该代码块时的值。
一句话概括,闭包是一个包含了上下文环境的匿名函数。

动态给按钮回调传入参数,如果缺少int cur = i;
进入按钮的回调,按任何参数都是for循环i最后一个最大值

由于使用了 lambda 表达式作为 AddListener 的参数,变量 i 成为了被 lambda 表达式捕获的外部变量,所以变量 i 将不会被作为垃圾回收,直至引用变量的委托符合垃圾回收的条件。

i 的最终取值是 m_listContent.Count,这导致所有按钮都被使用lm_listContent.Count,和需求不符,解决方法是在每一轮循环中都定义新的变量,这样每一次 lambda 表达式都捕获了不同的变量,避免闭包陷阱。

for (int i = 0; i < m_listContent.Count; i++){int cur = i;UIButton btn = m_listContent[i].GetComponent<UIButton>();btn.onClick.Add(new EventDelegate(delegate (){OnBtnGotoUrl(cur);//错误写法OnBtnGotoUrl(i);}));}

委托与事件

① 委托把一个方法作为参数代入另外一个方法,理解为函数指针
② 触发委托有2种方式: 委托实例.Invoke(参数列表),委托实例(参数列表)
③ 事件可以看作是一个委托类型的变量
④ 通过+=为事件注册多个委托实例或多个方法
⑤ 通过-=为事件注销多个委托实例或多个方法

delegate 是为了在C#中把函数作为对象传来传去而实现的一个“函数包装”,委托是具有相同签名的函数(方法)的类型。事件是委托的应用方式之,事件是一个属性/字段,类型是委托

delegate除了使用+=或-=来监听和移除方法,还可以用=,这样子使用会不小心把监听列表都覆盖掉的。
而event规范化了只能用+=和-=。

IDisposable

using

在 C# 中,using 语句是用于包裹一个实现 IDisposable 接口的对象的常见方式。IDisposable 接口提供了一种在使用完对象后释放资源的机制。
以下是一些常见的情况,在这些情况下你可以使用 using 语句来包裹对象:
1.文件操作:当你使用 FileStream、StreamReader、StreamWriter 等类进行文件读写时,通常会使用 using 来确保文件流在使用完后被正确关闭和释放资源。

using (FileStream fileStream = new FileStream("example.txt", FileMode.Open))
{// 使用文件流进行读写操作
}

2.数据库连接:当你使用 SqlConnection、SqlCommand、SqlDataReader 等类与数据库进行交互时,同样可以使用 using 来自动释放数据库连接和相关资源。

using (SqlConnection connection = new SqlConnection(connectionString))
{// 打开数据库连接并执行查询操作
}

3.网络请求:当你使用 HttpClient 或其他网络请求相关的类时,可以使用 using 来确保网络连接在使用完后被正确关闭。

using (HttpClient client = new HttpClient())
{// 发起网络请求
}

4.其他资源管理:任何实现了 IDisposable 接口的对象,如果需要在使用完后释放资源,都可以使用 using 语句来包裹。

using (SomeDisposableObject obj = new SomeDisposableObject())
{// 使用 obj 对象
}

使用 using 语句可以确保在代码块结束后,对象的 Dispose() 方法会被调用,从而释放资源。这样可以避免手动调用 Dispose() 方法或忘记释放资源的问题。

多次调Dispose

一个类型的Dispose方法应该允许被多次调用而不抛出异常。鉴于此,类型内部维护了一个私有的bool变量disposed,如下:

private bool m_Disposed;
/// <summary>/// 释放资源。/// </summary>public void Dispose(){Dispose(true);GC.SuppressFinalize(this);}/// <summary>/// 释放资源。/// </summary>/// <param name="disposing">释放资源标记。</param>private void Dispose(bool disposing){if (m_Disposed){return;}if (disposing){if (m_FileStream != null){m_FileStream.Dispose();m_FileStream = null;}}m_Disposed = true;}

GC.SuppressFinalize(this);
GC.SuppressFinalize 方法是用来通知垃圾回收器不要调用对象的析构函数(Finalize 方法)。它的作用是在对象已经被正确释放的情况下,避免不必要的资源回收操作,提高性能。
在 C# 中,当一个对象具有析构函数(Finalize 方法)时,垃圾回收器会在对象被垃圾回收之前调用该析构函数,以确保对象的资源得到正确释放。然而,在某些情况下,如果对象已经被显式地释放了,并且不再需要通过析构函数来释放资源,就可以使用 GC.SuppressFinalize 来通知垃圾回收器跳过对析构函数的调用。

不要创建过多线程

错误地创建过多线程的一个典型的例子是:为每一个Socket连接建立一个线程去管理。每个连接一个线程,意味着在32位系统的服务器不能同时管理超过约1000台的客户机。CLR为每个线程分配的内存会超过1MB。约1000个线程,加上.NET进程启动本身所占用的一些内存,即刻就耗尽了系统能分配给进程的最大可用地址空间2GB。即便应用程序在设计之初的需求设计书中说明,生产环境中客户端数目不会超过500台,在管理这500台客户端时进行线程上下文切换,也会损耗相当多的CPU时间。这类I/O密集型场合应该使用异步去完成

Parallel并行执行

在命名空间System.Threading.Tasks中,有一个静态类Parallel简化了在同步状态下的Task的操作。Parallel主要提供3个有用的方法:For、ForEach、Invoke。

static void Main(string[] args)  
{  int[] nums = new int[] { 1, 2, 3, 4 };  Parallel.For(0, nums.Length, (i) =>{  Console.WriteLine("针对数组索引{0}对应的那个元素{1}的一些工作代码……",i,  nums[i]);  });  Console.ReadKey();  
}

由于所有的任务都是并行的,所以它不保证先后次序。

Params传入参数

在 C# 中,使用 params 关键字作为函数参数传递不会直接导致垃圾回收(GC)。params 关键字所表示的参数数组是在编译期间就已经确定了大小并在运行时被创建的,不会引发额外的内存分配和释放操作。
当你调用带有 params 参数的函数时,编译器会将参数列表转换为一个数组,并将该数组传递给函数。这个数组在函数执行期间会存在于堆栈中,并在函数调用完成后被销毁。这个过程不会产生垃圾回收的开销。
然而,如果你在函数内部对 params 参数数组进行频繁的添加、插入、删除或修改等操作,这些操作可能会导致内存重新分配和释放,从而间接地增加垃圾回收的开销。因此,在设计代码时,应该尽量避免对 params 参数数组进行频繁的修改操作,或者考虑使用其他数据结构来替代 params 参数数组。
总的来说,params 参数本身不会直接产生垃圾回收,但如果在函数内部涉及到频繁的修改操作,可能会间接地增加垃圾回收的开销。因此,在设计和使用代码时,需要注意避免这些问题的出现。
还是有点难用,还是老实写多个函数重载吧

扩展方法

扩展方法除了让调用着可以像调用类型自身的方法一样去调用扩展方法外,它还有一些其他的主要优点:
可以扩展密封类型;
可以扩展第三方程序集中的类型;
扩展方法可以避免不必要的深度继承体系。
扩展方法还有一些必须遵循的要求:
扩展方法必须在静态类中,而且该类不能是一个嵌套类;
扩展方法必须是静态的;
扩展方法的第一个参数必须是要扩展的类型,而且必须加上this关键字;
不支持扩展属性、事件。
常见运用,C#中写设置Transform位置的扩展方法,给Lua调用,防止Lua传递Vector3造成性能消耗与类型转换

    public static void SetLocalPosition(this Transform transform, float x, float y, float z){transform.localPosition = new Vector3(x, y, z);}

接口与抽象类

接口和抽象类有一些显而易见的区别:
1.接口支持多继承,抽象类则不能。
2.接口可以包含方法、属性、索引器、事件的签名,但不能有实现,抽象类则可以。
3.接口在增加新方法后,所有的继承者都必须重构,否则编译不通过,而抽象类则不需要。
这些区别导致两者的应用场景不同:
1.如果对象存在多个功能相近且关系紧密的版本,则使用抽象类。
2.如果关系不紧密,但若干功能拥有共同的声明,则使用接口。
3.抽象类适合于提供丰富功能的场合,接口则更倾向于提供单一的一组功能。
从某种角度来看,抽象类比接口更具备代码的重用性。子类无须编写代码即可具备一个共性的行为。
采用抽象类的另一个好处是,如果为为基类增加一个方法,则继承该基类的所有子类自然就会具备这个额外的方法,而接口却不能。如果接口增加一个方法,必须修改所有的子类。所以,接口一旦设计出来就应该是不变的。抽象类则可以随着版本的升级增加一些功能。
接口的作用更倾向于说明类型具有某个或者某种功能。接口只负责声明,而抽象基类往往还要负责实现。
接口的职责必须单一,在接口中的方法应该尽可能的简练。

用多态代替条件语句

    abstract class Commander{public abstract void Execute();}class StartCommander : Commander{public override void Execute(){//启动}}class StopCommander : Commander{public override void Execute(){//停止}}static void Main(string[] args){Commander commander = new StartCommander();Drive(commander);commander = new StopCommander();Drive(commander);}static void Drive(Commander commander){commander.Execute();}

将类型标识为sealed

sealed能够阻止类型被其他类型继承

使用事件访问器替换公开的事件成员变量

public class MyClass 
{ // 声明事件private event EventHandler myEvent; // 定义事件访问器public event EventHandler MyEvent { add { myEvent += value; } remove { myEvent -= value; } }// 触发事件的方法protected virtual void OnMyEvent() { EventHandler handler = myEvent; if (handler != null) { handler(this, EventArgs.Empty); } } 
}

在上面的示例中,我们首先声明了一个私有的事件成员变量 myEvent,然后定义了一个公开的事件访问器 MyEvent。通过这个事件访问器,我们可以将事件添加到或从事件列表中删除事件。
在类中,使用 OnMyEvent() 方法来触发事件。该方法首先检查事件处理程序是否为空,如果不为空,则触发事件。

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

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

相关文章

系统添加深色模式实现方案

业务需求,夜间看系统太刺眼,要求添加夜间模式 效果如下: 依赖如下: 参考了官方解决方案,尝试后没有有效的解决. 官方解决方案 后续打算换框架,发现antdesign pro vue版本的暗黑模式禁用了. ant design pro 预览地址 思路: 引入andesign 暗黑模式的样式 , 手动修改自定义类…

在Linux上使用PHP-FPM与Nginx实现高效的HTTP处理

当谈到高效的HTTP处理时&#xff0c;PHP-FPM&#xff08;FastCGI进程管理器&#xff09;与Nginx的结合是许多web开发者的首选。这种组合提供了出色的性能、可扩展性和稳定性&#xff0c;尤其适用于高流量的网站和应用程序。 1. 为什么选择PHP-FPM与Nginx&#xff1f; 性能优化…

C++I/O流——(3)文件输入/输出(第一节)

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 含泪播种的人一定能含笑收获&#xff…

【python入门】day27: 模拟高铁售票系统

界面 代码 #-*- coding:utf-8 -*- import prettytable as pt#---------导入漂亮表格 import os.path filename ticket.txt#更新座位状态 def update(row_num):#------更新购票状态with open(filename,w,encodingutf-8) as wfile:for i in range(row_num):lst1 [f{i1},有票,有…

java自动化将用例和截图一起执行测试放入world中直接生成测试报告【搬代码】

1.首先我们得用例写好之后放入文档中,把不用的案例类型、前置条件去掉之后,如图: 放到桌面后,先看执行结果: 首先,我们先创建一个时间,这个时间主要是给图片创建名称,并且要在插入world中使用该时间去查找对应的图片名称,且该图片名称是唯一值 //创建时间public st…

原生js监听当前元素之外的点击事件

监听当前元素之外的点击事件 一、具体场景二、具体实现三、完整代码 一、具体场景 当我们需要实现点击其他位置关闭弹窗的时候&#xff0c;就需要监听当前元素之外的点击事件。 二、具体实现 实现思路&#xff1a;监听整个dom的点击事件&#xff0c;判断当前元素是否包含点击…

数据库重点简答题

文章目录&#xff08;持续更新&#xff09; 数据库重点简答题&#x1f4e3;一、SQL语言的作用&#xff1f;&#x1f4e3;二、说一下你对ER图的认识&#xff1f;&#x1f4e3;三、数据库中的三个模型&#xff1f;&#x1f4e3;四、数据库基本表和视图的区别和联系&#xff1f;&a…

Qt 信号和槽机制

一. 简介 在Qt中使用信号和槽机制来完成对象之间的协同操作。简单来说&#xff0c;信号和槽都是函数&#xff0c;比如按下窗口上的一个按钮后想要弹出一个对话框&#xff0c;那么就可以将这个按钮的单击信号和我们定义的槽关联起来&#xff0c;在这个槽中可以创建一个对话框&am…

Linux相关命令使用

一、Vi与Vim编辑器 1、正常模式 以vim打开一个档案就直接进入一般模式了&#xff0c;在这个模式中&#xff0c;你可以使用【上下左右】移动光标&#xff0c;可以使用【删除字符】或【删除整行】来处理档案内容&#xff0c;也可以使用【复制、粘贴】来处理你的文件数据。 2、…

训练DAMO-YOLO(damoyolo_tinynasL25_S.py)

文章目录 参考链接1 准备数据1.1 转为COCO格式1.2 指明数据路径 2 设置训练配置文件&#xff0c;在configs/damoyolo_tinynasL25_S.py进行如下两块修改2.1 关于训练参数的设置2.2 根据自己数据集设置 3 开始训练4 调用tools/eval.py进行测试5 训练时可能遇到的报错5.1 RuntimeE…

前端JS实现全屏和退出全屏的效果

全屏效果想必我们都很清楚把&#xff0c;平时追剧看电视剧什么都会使用全屏方便我们看&#xff0c;我们键盘的第一个键esc可以退出全屏&#xff0c;那么我们如何用js实现全屏的办法呢&#xff1f; 设置全屏 Document.requestFullscreen()&#xff0c;该方法用于异步请求使元素…

详解Matlab深度学习进行波形分割

&#x1f517; 运行环境&#xff1a;Matlab &#x1f6a9; 撰写作者&#xff1a;左手の明天 &#x1f947; 精选专栏&#xff1a;《python》 &#x1f525; 推荐专栏&#xff1a;《算法研究》 &#x1f510;#### 防伪水印——左手の明天 ####&#x1f510; &#x1f497; 大家…

【DB】Redis缓存优化策略之,缓存预热与缓存清除

文章目录 1、什么是缓存优化&#xff1f;2、缓存预热3、缓存清除/更新4、reids缓存预热与清除5、总结 1、什么是缓存优化&#xff1f; 缓存是一种数据存储技术&#xff0c;用于存储经常访问的数据&#xff0c;以便在需要时快速获取。通过缓存数据&#xff0c;可以减少数据的访…

mysql进阶-深度为2的B+Tree树能存储多少行数据?

目录 1. 非叶子节点计算 2. 叶子节点存储的是完整的数据。 3. 整体计算 BTree树需要计算有叶子节点和非叶子节点。 假设一张商品表的数据结构如下&#xff1a; CREATE TABLE t_good (good_id int(12) NOT NULL AUTO_INCREMENT COMMENT 主键,good_name varchar(20) DEFAULT…

深度学习的基本概念汇总

这里小小总结一下看论文时常见到的名词&#xff0c;不用再一个个搜索啦&#xff01;&#xff01;&#xff01; 1.batch size batch size是指在训练集中取的样本数&#xff0c;batch的size设置的不能太大也不能太小&#xff0c;因此实际工程中最常用的就是mini-batch&#xff0c…

C#编程-实现委托

实现委托 委托是可以存储对方法的引用的对象。在C#中,委托允许您动态地改变类中方法的引用。 考虑咖啡售货机的示例,它配置不同口味的咖啡,例如卡布奇诺咖啡和黑咖啡。在选择所需口味的咖啡时,售货机决定混合各种成分,例如奶粉、咖啡粉、热水、卡布奇诺咖啡粉。所有的材…

.Net Core 使用 AspNetCoreRateLimit 实现限流

上一篇文章介绍过ASP.NET Core 的 Web Api 实现限流 中间件-CSDN博客 使用.NET 7 自带的中间件 Microsoft.AspNetCore.RateLimiting 可以实现简单的Api限流&#xff0c;但是这个.NET 7以后才集成的中间件&#xff0c;如果你使用的是早期版本的.NET&#xff0c;可以使用第三方库…

React函数式组件学习笔记

React是一种用于构建用户界面的JavaScript库&#xff0c;它采用组件化的方式来构建复杂的UI。在React中&#xff0c;函数式组件是一种声明式的方式去描述UI的状态和行为。 React的特性 1.声明式设计-React采用声明范式&#xff0c;可以轻松描述应用 2.高效-React通过对DOM的模…

LaTeX系列1——主结构

初学&#xff0c;可交流&#xff0c;轻喷 \documentclass{book} \begin{document} \title{Book Title} \author{Author Name} \date{\today} \maketitle\chapter{Introduction} This is the introduction chapter of the book.\section{First Section} The first section of t…

【算法】使用栈解决一系列算法题(匹配、表达式、模拟)(C++)

1. 前言&#xff08;栈适用于解哪些题&#xff1f;&#xff09; 栈适合解决需要后进先出&#xff08;LIFO&#xff09;的结构的算法题&#xff0c;例如&#xff1a; 括号匹配问题&#xff1a;判断给定字符串中括号是否匹配。表达式求值问题&#xff1a;将表达式转换为后缀表达…