在C#的资源管理体系中,dispose和finalizer是两种用于释放非托管资源的核心机制,二者虽然都和资源清理相关,但在设计目标、执行逻辑、使用场景上存在本质差异,理解这些差异是写出正确资源管理代码的基础。

核心定义与本质差异
dispose是IDisposable接口定义的方法,属于主动的资源释放机制,由开发者显式调用或者在using语句块结束时自动触发,主要用于释放托管资源和非托管资源。而finalizer是对象的终结器,本质是Object类Finalize方法的重写,由垃圾回收器(GC)在回收对象时自动调用,属于被动的兜底清理机制,仅用于释放非托管资源。
触发时机对比
二者的触发时机完全不同,具体差异如下:
| 对比维度 | dispose | finalizer |
|---|---|---|
| 触发主体 | 开发者主动调用、using语句自动调用 | 垃圾回收器(GC)自动调用 |
| 触发时机 | 对象使用完毕后立即执行 | 对象成为垃圾且GC执行回收时执行,时机不确定 |
| 执行确定性 | 确定,调用后立即释放资源 | 不确定,依赖GC的回收节奏 |
使用规范与代码实现
dispose的标准实现
实现IDisposable接口时,通常需要区分托管资源和非托管资源的释放逻辑,同时提供防止重复释放的保护机制:
using System;
using System.Runtime.InteropServices;
public class ResourceHolder : IDisposable
{
// 模拟非托管资源句柄
private IntPtr _unmanagedHandle;
// 模拟托管资源
private IDisposable _managedResource;
// 标记是否已经释放
private bool _disposed = false;
public ResourceHolder()
{
// 初始化非托管资源
_unmanagedHandle = Marshal.AllocHGlobal(1024);
// 初始化托管资源
_managedResource = new SomeManagedResource();
}
// 实现IDisposable接口的Dispose方法
public void Dispose()
{
Dispose(true);
// 告诉GC不需要再调用finalizer
GC.SuppressFinalize(this);
}
// 受保护的虚方法,供子类重写
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing)
{
// 释放托管资源
_managedResource?.Dispose();
}
// 释放非托管资源
if (_unmanagedHandle != IntPtr.Zero)
{
Marshal.FreeHGlobal(_unmanagedHandle);
_unmanagedHandle = IntPtr.Zero;
}
_disposed = true;
}
}
finalizer的标准实现
finalizer的语法是C#中特殊的析构函数形式,仅用于释放非托管资源,不能访问托管对象(因为托管对象可能已经被GC回收):
using System;
using System.Runtime.InteropServices;
public class ResourceHolderWithFinalizer : IDisposable
{
private IntPtr _unmanagedHandle;
private bool _disposed = false;
public ResourceHolderWithFinalizer()
{
_unmanagedHandle = Marshal.AllocHGlobal(1024);
}
// finalizer,语法是析构函数形式
~ResourceHolderWithFinalizer()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
// finalizer调用时disposing为false,不释放托管资源
if (disposing)
{
// 这里可以释放托管资源
}
if (_unmanagedHandle != IntPtr.Zero)
{
Marshal.FreeHGlobal(_unmanagedHandle);
_unmanagedHandle = IntPtr.Zero;
}
_disposed = true;
}
}
关键注意事项
- 如果类持有非托管资源,建议同时实现
IDisposable接口和finalizer,dispose作为主动释放手段,finalizer作为兜底,防止开发者忘记调用dispose导致资源泄漏。 - 调用
Dispose方法后,一定要调用GC.SuppressFinalize(this),避免GC再次调用finalizer,提升性能。 - finalizer的执行会延迟对象的回收,因为对象在第一次GC标记后会被放入终结队列,需要第二次GC才会真正回收,因此不要随意定义finalizer。
- 不要在finalizer中访问托管对象,因为此时托管对象可能已经被回收,访问会导致不可预期的错误。
使用场景总结
当你的类仅持有托管资源时,不需要实现finalizer,只需要实现IDisposable接口即可,使用using语句管理生命周期。当类持有非托管资源时,必须同时实现IDisposable和finalizer,确保资源无论是否被主动释放都能得到清理。dispose是首选的资源释放方式,finalizer只是最后的保障手段,不能依赖finalizer来做常规的资源管理。
C#disposefinalizerIDisposable垃圾回收修改时间:2026-07-21 21:30:35