登录
/
注册
首页
论坛
其它
首页
科技
业界
安全
程序
广播
Follow
关于
导读
排行榜
资讯
发帖说明
登录
/
注册
账号
自动登录
找回密码
密码
登录
立即注册
搜索
搜索
关闭
CSDN热搜
程序园
精品问答
技术交流
资源下载
本版
帖子
用户
软件
问答
教程
代码
写记录
写博客
小组
VIP申请
VIP网盘
网盘
联系我们
发帖说明
道具
勋章
任务
淘帖
动态
分享
留言板
导读
设置
我的收藏
退出
腾讯QQ
微信登录
返回列表
首页
›
业界区
›
安全
›
net C# 如何理解和实现 Dispose 方法
net C# 如何理解和实现 Dispose 方法
[ 复制链接 ]
芮梦月
4 小时前
程序园永久vip申请,无限下载程序园所有程序/软件/数据/等
目录
.net C# 如何理解和实现 Dispose 方法
1、接口 IDisposable
2、析构函数
3、实现幂等
4、继承时的资源释放
5、性能改善
6、其他注意事项
(1)异常处理
(2)设置为 null
(3)字段 _disposed
(4)ObjectDisposedException
(5)线程安全
(6)异步释放
(7)一般情况
7、完整示例
.net C# 如何理解和实现 Dispose 方法
1、接口 IDisposable
接口 IDisposable 包含了一个名为 Dispose 的方法。
namespace System;
public interface IDisposable
{
void Dispose();
}
复制代码
实现了接口 IDisposable 的类,才可以使用 using 语句进行调用,以实现资源的释放,否则将报错错,提示:using 语句中使用的类型必须实现 System.IDisposable。
public class DbHelper : IDisposable
{
//...
public void Dispose()
{
//...
}
}
// 使用 using 语句进行调用
using (var helper = new DbHelper())
{
//...
}
// 使用 using 语句编译后等价于
DbHelper helper = null;
try
{
helper = new DbHelper();
//...
}
finally
{
helper?.Dispose();
}
复制代码
2、析构函数
C# 编译器不会为没有显式定义析构函数的类自动生成析构函数。只有当你显式定义了析构函数(~ClassName())时,编译器才会生成 Finalize 方法【析构函数是 C# 语法糖,最终编译为 Finalize 方法】。
析构函数的调用时机不可控。析构函数将仅用于释放非拖管资源。拖管资源由 GC 进行释放,释放时机和顺序不可控。若拖管资源再由析构函数来释放,则可能导致程序崩溃:已释放的资源(不存在的资源)在析构函数中再次被释放。
功能上,析构函数与 Dispose 方法重复进行了相同的工作。但二者角色不同,在实际的开发实践中,析构函数通常用于对
忘记主动调用 Dispose 方法
的补救。
因此,最佳实践是,将释放工作交给另一个方法 void Dispose(bool disposing),然后通过disposing 进行区分,到底调用是来自 Dispose 方法还是来自析构函数。示例如下:
public class DbHelper : IDisposable
{
//...
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// 释放托管资源
// ...
}
// 释放非托管资源(无论手动/GC 都必须执行)
// ...
}
~DbHelper()
{
Dispose(false);
}
}
复制代码
3、实现幂等
所谓幂等,即函数被调用次数不同,不影响结果。即,无论调用多少次,结果完全一样,不会报错、不会崩溃。
Dispose 方法必须实现幂等,因为代码里可能不小心多次调用 Dispose 方法。如果不做幂等,程序会报错、崩溃、资源重复释放。最佳实践中,资源释放工作交给了 void Dispose(bool disposing),因此,void Dispose(bool disposing) 实现幂等即可。
实现幂等的方式,比较简单,即如果已经释放资源,则不再进行资源释放工作,通过字段 private bool _disposed = false; 来实现。
public class DbHelper : IDisposable
{
//...
public void Dispose()
{
Dispose(true);
}
// 实现幂等的关键字段:
private bool _disposed = false;
protected virtual void Dispose(bool disposing)
{
//如果已经释放资源,则不再进行资源释放工作
if (_disposed) { return; }
if (disposing)
{
// 释放托管资源
// ...
}
// 释放非托管资源(无论手动/GC 都必须执行)
// ...
_disposed = true;
}
~DbHelper()
{
Dispose(false);
}
}
复制代码
4、继承时的资源释放
继承时,子类是需要释放父类实现中所占用的资源。因此,void Dispose(bool disposing) 最好应用 protected virtual 进行修饰,以便子类调用。
另,子类的 void Dispose(bool disposing) 也应具有幂等特性。在该方法上,父类与子类,应各自维护自己的幂等特性。
当类不需要被继承时( sealed 类),可以简化 Dispose 模式,即不需要用 virtual 修饰方法。
以下是子类示例:
public class Derived : DbHelper
{
// 其他 Derived 类特有的成员
// 注意:不要重新实现 Dispose() 方法,因为基类已经实现了 IDisposable
// 如果重新实现,会隐藏基类的 Dispose(),导致通过基类引用和子类引用调用 Dispose() 时行为不一致,破坏多态性。
// 只需重写 void Dispose(bool disposing) 方法即可
//public void Dispose()
//{
// Dispose(true);
//}
// 子类也应使 override 的 Dispose(bool disposing)实现幂等
private bool _disposed = false;
protected override void Dispose(bool disposing)
{
//如果已经释放资源,则不再进行资源释放工作
if (_disposed) { return; }
if (disposing)
{
// 释放 Derived 类特有的托管资源
// ...
}
// 释放 Derived 类特有的非托管资源(无论手动/GC 都必须执行)
// ...
// 调用基类的 Dispose 方法,确保基类资源也得到释放
base.Dispose(disposing);
_disposed = true;
}
// 子类不必实现析构函数,因为父类有析构函数。在析构的过程中,子类析构函数执行先于父类析构函数。
// 但无论如何,父类析构函数总会执行,又因为 override 了 void Dispose(bool disposing) 的缘故,
// 父类的析构函数中 Dispose(false) 语句,由于类的多态特性,将调用子类的 void Dispose(bool disposing) 方法。
// 仅调用 Dispose(false) 的子类析构函数,是冗余的。
//~Derived()
//{
// Dispose(false);
//}
}
复制代码
5、性能改善
有析构函数的对象会被放入 Finalization 队列,增加 GC 负担。
Dispose 方法用于主动地立即地资源释放,当我们主动释放资源后,垃圾回收器并不知情,因此需要知会垃圾回收器,请求不必调用终结器。
当类没有实现析构函数,不通过其进行兜底时,则不必知会垃圾回收器。如果没有析构函数,GC.SuppressFinalize(this);则是无意义的。
高性能场景
:避免使用析构函数。
public void Dispose()
{
Dispose(true);
// 请求系统不要调用这个对象的终结器,以提高性能
GC.SuppressFinalize(this);
}
复制代码
6、其他注意事项
(1)异常处理
禁止在 Dispose 中抛异常。因为可能会导致(编译生成的等效的) finally 块执行中断,进而资源泄漏,违背释放资源的初衷。
(2)设置为 null
纯托管对象(如 List),不需要手动释放,GC 会自动回收,设置为 null 不会立即释放内存,但可以断开引用,帮助 GC 更早发现对象不可达。其他的,如实现 IDisposable 的托管对象、非托管资源以及静态资源等,直接设置为 null 是无效的释放资源。
(3)字段 _disposed
它作为对象的字段存在,布尔类型,在系统调用析构函数时,它仍然没有消失,仍处理生命周期内。
(4)ObjectDisposedException
主动调用 Dispose 方法后,对象可能还在其生命周期中。如果此时调用对象的其他方法,则可能产生未知的错误。因此,需要在其他方法中检查是否已经释放资源。
public class DbHelper : IDisposable
{
private bool _disposed = false;
public void ExecuteQuery(string sql)
{
if (_disposed)
{
throw new ObjectDisposedException(nameof(DbHelper));
}
// 正常执行逻辑
}
}
复制代码
如果可行,最好在调用 Dispose 方法后,立即将对象设置为 null。
if (disposing)
{
_managedResource?.Dispose();
_managedResource = null;
}
复制代码
(5)线程安全
当需要时,void Dispose(bool disposing) 应实现其线程安全,避免多线程同时进入该方法从而导致错误发生。
protected virtual void Dispose(bool disposing)
{
lock (_lockObject)
{
if (_disposed) { return; }
if (disposing)
{
// 释放 Derived 类特有的托管资源
// ...
}
// 释放 Derived 类特有的非托管资源
// ...
base.Dispose(disposing);
_disposed = true;
}
}
复制代码
除使用上述锁方式外,还可使用 Interlocked 进行。
// 使用 Interlocked 的轻量级实现
private int _disposed = 0;
protected virtual void Dispose(bool disposing)
{
if (Interlocked.Exchange(ref _disposed, 1) == 0)
{
if (disposing)
{
// 释放托管资源
}
// 释放 Derived 类特有的非托管资源
// ...
// 释放非托管资源
base.Dispose(disposing);
}
}
复制代码
(6)异步释放
.NET Core 3.0+ 引入了异步释放模式,对于需要异步释放资源的场景(如异步文件操作、网络连接等)非常重要。
通过实现接口 IAsyncDisposable 的 DisposeAsync 方法的方式,提供了一种异步释放非托管资源的机制。
关于 异步释放资源的实现 与注意事项,此处略。
namespace System
{
public interface IAsyncDisposable
{
// 返回结果: 一个 task ,用于异步释放操作.
ValueTask DisposeAsync();// 非托管资源释放、异步释放或重置
}
}
复制代码
(7)一般情况
一般来说,实现了接口IDisposable 的类的实例,其释放应放在 disposing = true 中进行,因为它们已具有析构函数进行兜底。例如,数据库的连接的关闭。
部分类(如 DbConnection)有 Close 方法,本质是 Dispose 的封装。
if (disposing)
{
_managedResource?.Dispose();
_managedResource = null;
SqliteConn.Close();
}
复制代码
7、完整示例
public class DbHelper : IDisposable
{
//...
public void ExecuteQuery(string sql)
{
if (_disposed)
{
throw new ObjectDisposedException(nameof(DbHelper));
}
// 正常执行逻辑
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private bool _disposed = false;
protected readonly object _lockObject = new object();
protected virtual void Dispose(bool disposing)
{
lock (_lockObject)
{
if (_disposed) { return; }
if (disposing)
{
// 释放托管资源
// ...
}
// 释放非托管资源
// ...
_disposed = true;
}
}
~DbHelper()
{
Dispose(false);
}
}
public class Derived : DbHelper
{
// 其他 Derived 类特有的成员
//...
private bool _disposed = false;
protected override void Dispose(bool disposing)
{
//基类和子类使用相同的锁对象,保证整个释放过程是原子的
lock (_lockObject)
{
if (_disposed) { return; }
if (disposing)
{
// 释放 Derived 类特有的托管资源
// ...
}
// 释放 Derived 类特有的非托管资源
// ...
base.Dispose(disposing);
_disposed = true;
}
}
}
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
net
如何
理解
实现
Dispose
相关帖子
简明教程:实现OpenCLaw轻量级应用服务器部署及Ollama大模型本地化
.NET 磁盘BitLocker加密-技术选型
老板与员工:5分钟理解 Subagent 架构
在Python中用any-singleton实现单例模式
【译】 如何使用 .NET MAUI 构建 iOS 小部件
基于.NET AgentFramework开发OpenClaw智能体框架
前端如何防止用户重复提交表单?4 种可靠方案(附防坑指南)
.NET对象转JSON,到底有几种方式?
基于.NET的Windows窗体编程之WinForms入门简介
回复
使用道具
举报
提升卡
置顶卡
沉默卡
喧嚣卡
变色卡
千斤顶
照妖镜
相关推荐
业界
简明教程:实现OpenCLaw轻量级应用服务器部署及Ollama大模型本地化
0
475
鸠站
2026-03-20
业界
.NET 磁盘BitLocker加密-技术选型
0
785
吞脚
2026-03-20
业界
老板与员工:5分钟理解 Subagent 架构
0
589
搁胱
2026-03-20
业界
在Python中用any-singleton实现单例模式
0
664
嗦或
2026-03-21
业界
【译】 如何使用 .NET MAUI 构建 iOS 小部件
0
984
剩鹄逅
2026-03-21
安全
基于.NET AgentFramework开发OpenClaw智能体框架
0
333
秤陷曲
2026-03-23
代码
前端如何防止用户重复提交表单?4 种可靠方案(附防坑指南)
0
534
茹静曼
2026-03-24
业界
.NET对象转JSON,到底有几种方式?
0
313
吮槌圯
2026-03-24
安全
基于.NET的Windows窗体编程之WinForms入门简介
0
814
吟氅
2026-03-24
高级模式
B
Color
Image
Link
Quote
Code
Smilies
您需要登录后才可以回帖
登录
|
立即注册
回复
本版积分规则
回帖并转播
回帖后跳转到最后一页
浏览过的版块
业界
签约作者
程序园优秀签约作者
发帖
芮梦月
4 小时前
关注
0
粉丝关注
28
主题发布
板块介绍填写区域,请于后台编辑
财富榜{圆}
3934307807
991125
anyue1937
9994892
kk14977
6845359
4
xiangqian
638210
5
神泱
9522
6
韶又彤
9916
7
宋子
9878
8
荪俗
9016
9
闰咄阅
9995
10
刎唇
9995
查看更多
今日好文热榜
271
面试一问就给我整不会了如何跨标签页通信
956
单调队列优化多重背包 学习笔记 & 详解
477
OpenClaw+优云智算Coding Plan:从灵感到成
866
net C# 如何理解和实现 Dispose 方法
839
归并排序力扣题(leetcode)
400
龙芯2k0300 - 走马观碑组ST7735驱动移植
982
大模型智能体 (agent)简易流程介绍
517
3.Langchain 1.2.0 学习 --- LCEL和Runnabl
286
搭建产品原型
974
AI Coding 实战:10年祖传系统,54万行代码
393
微信对接OpenClaw的常见问题和解决方案
380
一文学习 工作流开发 BPMN、 Flowable
904
3.Acwing基础课第787题-简单-归并排序
242
Spring Boot 3.x 整合AI大模型实战:手把手
803
Python AI爬虫实战:爬取张雪峰微博并进行
117
龙虾白嫖指南,请查收~
281
2.归并排序:稳定的分治排序
4
2.归并排序:稳定的分治排序
4
2.归并排序:稳定的分治排序
5
2.归并排序:稳定的分治排序