C#基础-资源清理-终结器与IDisposable

内容借鉴-C#8.0本质论

终结器

终结器 (finalizer) 允许程序员写代码来清理类的资源。与使用new操作符显式调用构造函数不同,终结器不能从代码中显式调用。没有和new对应的操作符 (比如像delete这样的操作符)。相反,是垃圾回收器负责为对象实例调用终结器。因此,开发者不能在编译时确定终结器的执行时间。唯一确定的是终结器会在对象最后一次使用之后,并“通常”在应用程序正常关闭前的某个时间运行。这里“通常”一词是为了强调事实上终结器有可能不会被调用,尤其当程序被强行关闭时。例如,计算机关机,或者程序被调试器强行终止,终结器都很有可能不会被调用。

  • 再次强调,终结器不能显式调用,只有垃圾回收器才能调用。

一、终结器的定义

终结器的声明与构造函数类似,但要求在类名之前添加一个~字符。终结器不允许传递任何参数,所以不可重载。此外,终结器不能显式调用。调用终结器的只能是垃圾回收器。因此,为终结器添加访问修饰符没有意义 (也不支持) 。基类中的终结器作为对象终结调用的一部分被自动调用。

public class FinalizerTest
{~FinalizerTest() { ... }
}

由于垃圾回收器负责所有内存管理,所以终结器不负责回收内存。相反,它们负责释放像数据库连接文件句柄这样的资源这些资源需通过一次显式的行动来进行清理,而垃圾回收器不知道具体如何采取这些行动。

在设计程序时,应该避免实现没有必要的终结器。

二、终结器的示例

public class FinalizerTest
{public FileStream? Stream { get; private set; }public FileInfo? File { get; private set; }public FinalizerTest() : this(Path.GetTempFileName()) { }public FinalizerTest(string fileName){File = new FileInfo(fileName);Stream = new FileStream(File.FullName, FileMode.OpenOrCreate, FileAccess.ReadWrite);}~FinalizerTest(){try{Close();}catch (Exception ex){//异常处理}}public void Close(){Stream?.Dispose();try{File?.Delete();}catch (IOException exception){Console.WriteLine(exception);}Stream = null;File = null;}
}

示例中,终结器的主要目的是调用File.Delete(),将在构造函数中创建的临时文件删除。但是它首先调用了FileStream对象的Dispose()方法将该对象销毁。FileStream类有自己的终结器,并具有与Dispose()相同的效果,因此通常不是必须要调用其Dispose()方法。但在这里我们需要确保该FileStream对象的销毁先于File.Delete()发生,否则Delete()无法删除文件。反之,如果不通过实现终结器,主动销毁FileStream对象,则不能预测它的终结器与FinalizerTest对象的终结器哪一个先被调用,导致不能预测文件能否被删除

IDisposable

注意终结器在自己的线程中执行,这使它们的执行变得更不确定。这种不确定性使终结器中(调试器外)未处理的异常变得难以诊断,因为造成异常的情况是不确定的。从用户的角度看,未处理异常的引发时机显得相当随机,跟用户当时执行的任何操作都没有太大关系。有鉴于此,一定要在终结器中避免异常。为此需采取一些防卫性编程技术,比如空值检查等。实际上,通常应该在终结器中捕获所有可能发生的异常,并通过其他途径进行汇报,而不是放任这些异常逃逸,留给系统默认处理。更好的方式是将异常捕获并记录在日志文件中或者显示在用户界面的诊断信息区等。就这个原因,上面的示例代码中将File.Delete()包围在try/catch块中。

终结器的问题在于不支持确定性终结(也就是预知终结器在何时运行)。相反,终结器是对资源进行清理的备用机制。假如类的使用者忘记显式调用必要的清理代码就得依赖终结器清理资源。

因此,很有必要提供进行确定性终结的方法,来避免依赖终结器不确定的行为。

  • 例如终结器的示例中,即使开发者忘记显式调用Close(),终结器也会调用它。虽然终结器运行得会晚一些 (相较于显式调用Close() ),但该方法肯定会得到调用。此时Close()就是确定性终结的方法。

由于确定性终结的重要性,基类库为这个使用模式包含了特殊接口,而且C#已将这个模式集成到语言中。IDisposable接口用名为Dispose()的方法定义了该模式的细节。

将上面的终结器中的示例进一步优化,实现IDisposable接口,通过Dispose()来提供确定性终结的方法。

 using FinalizerTest ft = new FinalizerTest("D://test.txt");public class FinalizerTest : IDisposable{public FileStream? Stream { get; private set; }public FileInfo? File { get; private set; }public FinalizerTest() : this(Path.GetTempFileName()) { }public FinalizerTest(string fileName){File = new FileInfo(fileName);Stream = new FileStream(File.FullName, FileMode.OpenOrCreate, FileAccess.ReadWrite);}~FinalizerTest(){Dispose(false);}public void Dispose(){Dispose(true);// Unregister from the finalization queueGC.SuppressFinalize(this);}public void Dispose(bool disposing){if (disposing){Stream?.Close();}try{File?.Delete();}catch (Exception ex){Console.WriteLine(ex.Message);}Stream = null;File = null;}}

关于using

using语句只是提供了try/finally块的语法快捷方式,并且在finally中会执行对应对象的Dispose()方法。也就是说,使用using的变量必须都实现了IDisposable接口。

using语句目前有两种使用方式,一种是使用using{},可以一次性生命多个变量,用逗号隔开;另一种是在C#8.0之后才出现的,直接在声明变量之前使用using关键字,但每次只能声明一个变量。此外,用using关键字声明的变量还有一个限制,就是这种变量不能再被赋值为其他值。

示例

using FinalizerTest ftA = new FinalizerTest(), ftB = new FinalizerTest()
{//....
};using FinalizerTest ftC = new FinalizerTest();

示例中有如下几个要点需要认真关注:

  • 示例中,调用了System.GC.SuppressFinalize(),作用是从终结 (f-reachable) 队列中移除FinalizerTest类实例。这是因为所有清理都在Dispose()方法中完成了,而不是等着终结器执行。如果不调用SuppressFinalize(),对象的实例会包括到f-reachable队列中。该队列中的对象已差不多准备好了进行垃圾回收,只是它们还有终结方法没有运行。这种对象只有在其终结方法被调用之后,才能由“运行时”进行垃圾回收。但垃圾回收器本身不调用终结方法。相反,对这种对象的引用会添加到f-reachable队列中,并由一个额外的线程根据执行上下文,挑选合适的时间进行处理。这就造成了托管资源的垃圾回收时间的推迟,而许多这样的资源本应更早一些被清理。推迟是因为f-reachable队列是“引用”列表。所以,对象只有在它的终结方法得到调用,而且对象引用从f-reachable队列中删除之后,才会真正变成“垃圾”。需要注意的是,有终结器的对象如果不显式dispose,其生存期会被延长,因为即使对它的所有显式引用都不存在了,f-reachable队列仍然包含对它的引用,使对象直生存,直至f-reachable队列处理完毕。正是由于这个原因,Dispose()才调用System.GC.SuppressFinalize(),告诉“运行时”不要将该对象添加到f-reachable队列而是允许垃圾回收器在对象没有任何引用(包括任何f-reachable引l用) 时清除对象。
  • Dispose()调用了Dispose(bool disposing)方法,并传递实参true。结果是为Stream调用Dispose()方法 (清理它的资源并阻止终结)。接着,临时文件在调用Dispose()后立即删除。这个重要的调用避免了一定要等待终结队列处理完毕才能清理资源的限制。
  • 终结器现在不是调用Close()而是调用Dispose(bool disposing),并传递实参false。结果是即使文件被删除,Stream也不会关闭 (disposed) 。原因是从终结器中调用Dispose(bool disposing)时,Stream实例本身还在等待终结 (或者已经终结,系统会以任意顺序终结对象)所以,在执行终结器时,拥有托管资源的对象不应清理,那应该是终结队列的职责。

设计规范

在使用终结器和实现IDisposable接口的过程中,有如下几点设计规范:

  • 要只为使用了稀缺或昂贵资源的对象实现终结器方法,即使终结会推迟垃圾回收。
  • 要为有终结器的类实现IDisposable接口以支持确定性终结。
  • 只有当类包含必须释放的资源,而该资源自己又没有终结器的时候,才要在类中实现终结器。
  • 要重构终结器方法来调用与IDisposable相同的代码,可能就是调用一下Dispose()方法。
  • 不要在终结器方法中抛出异常。
  • 要从Dispose()中调用System.GCSuppressFinalize(),以使垃圾回收更快地发生,并避免重复性的资源清理。
  • 要保证Dispose()可以重入 (可被多次调用)。
  • 要保持Dispose()的简单性,把重点放在终结所要求的资源清理上。
  • 避免为自己拥有的、带终结器的对象调用Dispose()。相反,依赖终结队列清理实例。
  • 避免在终结方法中引用未被终结的其他对象。
  • 要在重写Dispose()时调用基类的实现。
  • 考虑在调用Dispose()后将对象状态设为不可用。对象被dispose之后,调用除Dispose()之外的方法应发ObjectDisposedException异常(Dispose方法则能多次调用)。
  • 要为含有可dispose字段 (或属性)的类型实现IDisposable接口,并dispose这些字段引用的对象。
  • 要在派生类的Dispose()中调用基类的Dispose()
  • 若要提高终结器在程序结束之前被调用的可能性,考虑将终结器中的代码实现在事件处理器里并注册到AppDomain.CurrentDomain.ProcessExitevent中。
  • 如果一个类同时注册了AppDomain.CurrentDomain.ProcessExitevent事件,又实现了Dispose(),一定要在Dispose()方法中解除注册的事件。

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

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

相关文章

使用git工具把项目文件上传到github 的操作

先打开git-bash进入操作界面 cd D: 进入d盘 mkdir myrepo 创建文件 cd myrepo 进入本地仓库文件 git init 初始仓库 (rm -rf .git 取消 git init 命令。) git add . git status (显示你修改的文件具体是哪些&#xff0…

智能分析网关V4基于AI视频智能分析技术的周界安全防范方案

一、背景分析 随着科技的不断进步,AI视频智能检测技术已经成为周界安全防范的一种重要手段。A智能分析网关V4基于深度学习和计算机视觉技术,可以通过多种AI周界防范算法,实时、精准地监测人员入侵行为,及时发现异常情况并发出警报…

text expressing

文章目录 前言文本表示1文本特征概念介绍2 文本特征选择方法3 文本表示方法 text expressing3.1 One Hot(独热)编码3.2 TF-IDF 模型3.3 Word2Vec 参考链接: 前言 文本是一种非结构化的数据信息,是不可以直接被计算的。 文本表示的作用就是将这些非结构…

解密SHFileOperation

解密SHFileOperation 大家好,我是免费搭建查券返利机器人赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!在今天的文章中,我们将揭开Windows文件操作的神秘面纱,介绍一款强大而神…

powershell的help

打开win10 的powershell窗口,输入help命令,可以得到如下说明: 有了help系统,可以方便地了解关于powershell的详细说明。

文献阅读(速读):Automating Deep Neural Network Model Selection for Edge Inference

目录 论文简介动机:为什么作者想要解决这个问题?贡献:作者在这篇论文中完成了什么工作(创新点)?规划:他们如何完成工作?自己的看法(作者如何得到的创新思路) 论文简介 作者 Bingqian Lu、Jianyi Yang、Lydi…

cesium设置近地天空盒 天空会倾斜

上篇文章讲解了如何设置近地天空盒,效果出来了还是发现天空是斜的 https://blog.csdn.net/m0_63701303/article/details/135618244 效果: 这里需要修改Cesium.skyBox的代码,代码如下直接全部复制组件内调用即可 skybox_nearground.js&…

申请代码签名证书有什么需要注意的地方?

在申请代码签名证书之前,开发者需要注意一些重要的地方,以确保申请顺利进行并保证证书的有效性和安全性。 首先,选择可信赖的认证机构(CA)是非常重要的。认证机构是颁发代码签名证书的机构,其信誉和声誉直接…

《GreenPlum系列》GreenPlum初级教程-04GreenPlum数据类型

第四章 GreenPlum数据类型 1.基本数据类型 1.1 数值类型 类型名称存储空间描述范围smallint2字节小范围整数-32768 ~ 32767integer4字节常用的整数-2147483648~2147483647bigint8字节大范围的整数-9223372036 854 ~9223372036854decimal变长用户声明精度,精确无限…

分布式光伏运维平台在提高光伏电站发电效率解决方案

摘要:伴随着能源危机和环境恶化问题的日益加重,科技工作者进一步加大对新能源的开发和利用。太阳能光伏发电作为新型清洁能源的主力军,在实际生产生活中得到了广泛的应用。然而,光伏发电效率偏低,成为制约光伏发电发展…

机器学习算法实战案例:LSTM实现单变量滚动风电预测

文章目录 1 数据处理1.1 数据集简介1.2 数据集处理 2 模型训练与预测2.1 模型训练2.2 模型滚动预测2.3 结果可视化 答疑&技术交流机器学习算法实战案例系列 1 数据处理 1.1 数据集简介 实验数据集采用数据集5:风电机组运行数据集,包括风速、风向、…

【软件测试学习笔记3】缺陷管理

执行结果和预期结果不一样,就叫缺陷,俗称bug 1.软件缺陷判定标准 少功能:软件未实现需求(规格)说明书中明确要求的功能 功能错误:软件出现了需求(规格)说明书中指明不应该出现的错…

cookie、Web Storage

前端知识汇编 1. cookie1.1 cookie的限制1.2 cookie的构成1.3 JavaScript中的cookie1.4 子cookie1.5 使用cookie的注意事项 2. Web Storage2.1 Storage类型2.2 sessionStorage对象2.3 localStorage对象2.4 存储事件2.5 限制 1. cookie cookie是客户端与服务器端进行会话时使用…

自写代码来理解 get_global_id 和 get_global_size

<2022-01-24 周一> 《OpenCL编程指南》第三章 自写代码来理解get_global_id和get_global_size 使用本书第三章中关于输入信号卷积的代码来进行理解&#xff0c;见随书代码“src/Chapter_3/OpenCLConvolution”&#xff0c;附代码如下&#xff1a; // // Book: O…

webpack打包可视化分析工具:webpack-bundle-analyzer

在对webpack项目进行优化的时候,可以使用webpack-bundle-analyzer这个可视化插件来快速分析我们包的结构,能快速定位需要优化的地方,对开发者非常友好 下载安装 下载依赖包 npm i webpack-bundle-analyzer 使用 const BundleAnalyzerPlugin require(webpack-bundle-analy…

qt.qpa.plugin: Could not find the Qt platform plugin “windows“ in ““

系统环境&#xff1a;Win10家庭中文版 Qt : 5.12.9 链接了一些64位的第三方库&#xff0c;程序编译完运行后出现 qt.qpa.plugin: Could not find the Qt platform plugin "windows" in "" 弹窗如下&#xff1a; 网上搜了一些都是关于pyQt的&#xff0c…

【实战记录】 vagrant+virtualbox+docker 轻松用虚拟机集成组件

用途 最近要学一大堆组件&#xff0c;不想直接安装本机上&#xff0c;然后gpt说&#xff1a;你可以用vagrant起个虚拟机&#xff08;然后docker拉取各种组件的镜像&#xff09;&#xff1b;或者k8s 实战的整体思路 首先安装virtualbox和vagrant。然后cmd依次键入三条命令 安…

负载均衡 LoadBalancer

负载均衡 负载均衡一般分为服务端负载均衡和客户端负载均衡 服务端负载均衡&#xff1a; 指在服务器端进行负载均衡的策略。在这种策略下&#xff0c;负载均衡器位于服务器端&#xff08;如 Nginx&#xff09;&#xff0c;当客户端发起服务调用时&#xff0c;根据服务器的负…

旧衣回收小程序搭建:降低企业成本,提高回收效率!

在人们环保意识提升下&#xff0c;旧衣回收行业受到了大众的关注&#xff0c;同时旧衣回收具有门槛低、利润大的优势。在我国&#xff0c;回收行业不仅帮助普通人就业获利&#xff0c;还对环保做出了较大贡献。因此&#xff0c;旧衣回收行业成为了当下的热门商业模式&#xff0…

时尚女童冲锋衣外套

上身时尚又好看的外套 日常穿着或者出行游玩 应对早晚温差&#xff0c;兼具时尚和功能 保暖也可以很轻盈 率性闲适的洒脱范 版型百搭好穿 下摆有橡筋收紧更加保暖了 简直就是一件实用与时尚并存的时尚单品