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

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()**。只有当你类里直接持有非托管资源(比如 IntPtr、SafeHandle 子类、P/Invoke 返回的句柄),且没用现成的托管包装(如 FileStream 已帮你管好了),才需要加 Finalize 作为兜底。
- ✅ 推荐场景:封装了数据库连接、文件句柄、Socket、GDI 对象(
HBITMAP、HDC)等 —— 实现IDisposable,并在Dispose(true)中释放它们 - ❌ 典型误用:类里只用了
List、Dictionary等纯托管对象 —— 完全不需要IDisposable,更别提Finalize - ⚠️ 注意:如果你的类包含一个
IDisposable成员(比如自己 new 出的MemoryStream),那你也要实现IDisposable,并在自己的Dispose()里调用它的Dispose()
标准 Dispose 模式里,disposing 参数到底控制什么?
这个布尔参数不是摆设 —— 它决定了当前调用是不是来自你手动写的 Dispose()(true),还是来自 GC 的 Finalize(false)。这是区分“能安全操作托管资源”和“只能碰非托管资源”的唯一开关。
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 线程上调的,此时托管资源可能已不可用,只处理IntPtr、SafeHandle、CloseHandle这类底层操作 - 漏掉
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() 写对、用对、测对。








