0

0

c# dispose 和 finalizer 的区别

煙雲

煙雲

发布时间:2026-01-03 10:03:08

|

495人浏览过

|

来源于php中文网

原创

Dispose是可控、可预测的资源释放,Finalize是GC在不确定时机触发的被动补救机制;前者需实现IDisposable并显式调用,后者为语法糖且不应手动调用。

c# dispose 和 finalizer 的区别

Dispose 是你主动喊“收摊”,Finalize 是 GC 偷偷帮你扫尾

根本区别就一句话:Dispose() 是你控制的、可预测的资源释放;Finalize(即析构函数 ~ClassName())是 GC 在不确定时间、不确定线程上被动触发的“补救机制”。你不调用 Dispose(),程序可能跑着跑着就卡住或报“无法访问已关闭的文件”这类错误;你不写 Finalize,只要 Dispose() 写对了,系统照样稳如老狗。

  • Dispose() 必须实现 IDisposable 接口,由你显式调用(比如 obj.Dispose() 或用 using 语句块)
  • Finalize 是 C# 语法糖,编译后变成 protected override void Finalize(),GC 自动调用,你不能直接调用它(也不该尝试)
  • GC 调用 Finalize 至少要等两次垃圾回收:第一次标记 + 放入 freachable 队列,第二次才真正执行 —— 这意味着非托管资源可能被锁住几十毫秒甚至几秒
  • 如果对象有 Finalize,它的生命周期会被拉长,拖慢 GC 效率,还可能引发对象间析构顺序错乱(比如 A 析构时访问了已被 B 析构掉的句柄)

什么时候必须写 Dispose?什么时候可以不写 Finalize?

绝大多数情况下:只实现 Dispose(),**完全不用写 ~ClassName()**。只有当你类里直接持有非托管资源(比如 IntPtrSafeHandle 子类、P/Invoke 返回的句柄),且没用现成的托管包装(如 FileStream 已帮你管好了),才需要加 Finalize 作为兜底。

  • ✅ 推荐场景:封装了数据库连接、文件句柄、Socket、GDI 对象(HBITMAPHDC)等 —— 实现 IDisposable,并在 Dispose(true) 中释放它们
  • ❌ 典型误用:类里只用了 ListDictionary 等纯托管对象 —— 完全不需要 IDisposable,更别提 Finalize
  • ⚠️ 注意:如果你的类包含一个 IDisposable 成员(比如自己 new 出的 MemoryStream),那你也要实现 IDisposable,并在自己的 Dispose() 里调用它的 Dispose()

标准 Dispose 模式里,disposing 参数到底控制什么?

这个布尔参数不是摆设 —— 它决定了当前调用是不是来自你手动写的 Dispose()true),还是来自 GC 的 Finalizefalse)。这是区分“能安全操作托管资源”和“只能碰非托管资源”的唯一开关。

网钛淘拍CMS(TaoPaiCMS) V1.60
网钛淘拍CMS(TaoPaiCMS) V1.60

2013年07月06日 V1.60 升级包更新方式:admin文件夹改成你后台目录名,然后补丁包里的所有文件覆盖进去。1.[新增]后台引导页加入非IE浏览器提示,后台部分功能在非IE浏览器下可能没法使用2.[改进]淘客商品管理 首页 列表页 内容页 的下拉项加入颜色来区别不同项3.[改进]后台新增/修改淘客商品,增加淘宝字样的图标和天猫字样图标改成天猫logo图标4.[改进]为统一名称,“分类”改

下载
protected virtual void Dispose(bool disposing)
{
    if (!disposed)
    {
        if (disposing)
        {
            // ✅ 这里可以安全调用其他托管对象的 Dispose()
            // ✅ 可以释放事件订阅、取消定时器、关闭托管流
            _stream?.Dispose();
            _timer?.Dispose();
        }
        // ⚠️ 这里只能释放非托管资源!
        // ❌ 绝对不要在这里访问任何托管对象(比如 _stream.Length),因为它们可能已被 GC 回收
        if (_handle != IntPtr.Zero)
        {
            CloseHandle(_handle);
            _handle = IntPtr.Zero;
        }
        disposed = true;
    }
}
  • disposing == true:说明是人调的 Dispose(),托管资源大概率还活着,可以放心清理
  • disposing == false:说明是 GC 在 finalizer 线程上调的,此时托管资源可能已不可用,只处理 IntPtrSafeHandleCloseHandle 这类底层操作
  • 漏掉 GC.SuppressFinalize(this) 是高频失误:一旦手动调过 Dispose(),就该立刻告诉 GC “别再费劲调我的析构函数了”,否则等于白写优化

为什么 using 语句比 try/finally + Dispose() 更可靠?

因为 using 编译后会自动插入 try/finally 并确保 Dispose() 执行 —— 即使中间抛异常、提前 return、甚至 Environment.FailFast(),它都守得住。

using (var fs = new FileStream("log.txt", FileMode.Append))
{
    fs.Write(buffer, 0, buffer.Length);
    // 即使这里 throw new InvalidOperationException();
    // fs.Dispose() 仍会被调用
}
  • using 要求类型必须实现 IDisposable,否则编译失败 —— 这本身就是一层静态检查
  • 避免手写 try/finally 时漏掉 Dispose() 或在 catch 后忘记调用(尤其多层嵌套时)
  • 注意:如果构造函数就抛异常(比如文件被占用),using 块内变量为 null,但 Dispose() 不会调用 —— 所以资源分配失败本身不依赖 Dispose 清理

Finalize 和 Dispose 的边界其实很清晰:前者是留给系统兜底的退路,后者是你作为开发者该扛起的责任。现实中,95% 的 .NET 开发者一辈子都不需要亲手写 ~ClassName() —— 但每个用到文件、数据库、网络、图形资源的人,都得把 Dispose() 写对、用对、测对。

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

313

2023.08.02

c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

230

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

434

2024.03.01

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

313

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

522

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

49

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

190

2025.08.29

javascriptvoid(o)怎么解决
javascriptvoid(o)怎么解决

javascriptvoid(o)的解决办法:1、检查语法错误;2、确保正确的执行环境;3、检查其他代码的冲突;4、使用事件委托;5、使用其他绑定方式;6、检查外部资源等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

173

2023.11.23

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

150

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Java 教程
Java 教程

共578课时 | 41.1万人学习

国外Web开发全栈课程全集
国外Web开发全栈课程全集

共12课时 | 0.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号