IDisposable 接口
1. 托管资源和非托管资源
· 托管资源
a. CLR 控制和管理的内存资源,如程序中在 Heap 上分配的对象、作用域内的变量等;
b. GC 机制实现自动内存管理和托管堆的全权管理;
· 非托管资源
a. CLR 不能控制管理的部分,如文件流Stream/数据库连接coonection/窗口句柄/组件COM等;
b. Finalize 方法(析构函数) GC 隐式自动调用,Dispose 方法手动强制显式调用;
c. 尽量避免使用 Finalize() 方法清理资源,推荐实现 Dispose() 方法供显式调用;
注:MSDN - 实现 Finalize() 方法或析构函数对性能可能会有负面影响。用 Finalize() 方法回收对象占用的内存至少需要两次垃圾回收,第一次调用析构函数,第二次删除对象。 GC 机制在回收托管对象内存之前,会先调用对象的析构函数。
2. 析构函数(Finalize方法) .vs. Dispose方法
Finalize 方法用于释放非托管资源,Dispose 方法用于清理或释放由类占用的非托管和托管资源。IDisposable 接口定义见上,自定义类应实现 IDisposable 接口,设计原则:
· 可以重复调用 Dispose() 方法;
· 析构函数应该调用 Dispose() 方法;
· Dispose() 方法应该调用 GC.SuppressFinalize() 方法,指示垃圾回收器不再重复回收该对象;
在一个包含非托管资源的类中,资源清理和释放的标准模式是:
1. 继承 IDisposable 接口;
2. 实现 Dispose() 方法,在其中释放托管和非托管资源,并将对象从垃圾回收器链表中移除;
3. 实现类的析构函数,在其中释放非托管资源;
其中,变量 "isDisposing" 来区分手动显式调用(true)还是GC隐式调用(false)。
public class MyDispose : IDisposable
{
public MyDispose() { }
~MyDispose() {
Dispose(false);
}
private bool isDisposed = false;
public void Dispose(){
Dispose(true);
System.GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool isDisposing) // 子类可重写
{
if (false == this.isDisposed)
{
if (true == isDisposing){
OtherManagedObject.Dispose(); // 释放托管资源 ...
}
OtherUnManagedObjectDisposeOrClose(); // 释放非托管资源 ...
this.isDisposed = true;
}
}
}
析构函数执行在类的实例被销毁之前需要的清理或释放非托管资源的行为,注意不能在析构函数中释放托管资源。类的析构函数被编译后自动生成 protected void Finalize() 方法,GC 垃圾回收时会调用该方法并对继承链中的所有实例递归地调用 Finalize() 方法。Object.Finalize() 方法不可重写。
· 类的析构函数不可继承和重载、不能带访问修饰符,一个类至多有一个析构函数;
· 析构函数只针对类的实例对象,没有静态析构函数;
protected void Finalize(){
try{
//
}
finally{
base.Finalize();
}
}
Finalize() 方法被调用的情况:
· 显式调用System.GC 的 Collect方法(不建议);
· Windows 内存不足、第G0代对象充满;
· 应用程序被关闭或 CLR 被关闭;
Dispose() 方法的调用分 2 种:
· 使用 using 语句会自动调用:using( MyDispose myObj = new MyDispose() ) {…}
· 显式调用:myObj.Dispose();
一个资源安全的类,都应实现 IDisposable 接口和析构函数,提供手动释放资源和系统自动释放资源的双保险。(1)若一个类A有一个实现了 IDisposable 接口类型的成员并创建(创建而不是接收,必须是由类A创建)它的实例对象,则类A也应该实现 IDisposable 接口并在 Dispose 方法中调用所有实现了 IDisposable 接口的成员的 Dispose 方法;(2)如果基类实现了 IDisposable 接口,那么其派生类也要实现 IDisposable 接口,并在其 Dispose 方法中调用基类中 Dispose 方法;只有这样才能保证所有实现了 IDisposable 接口的类的对象的 Dispose 方法能被调用到、手动释放任何需要释放的资源。