C# 高级编程 - 释放资源
Dispose方式
微软为非托管资源的回收专门定义了一个接口:IDisposable,接口中只能包含一个Dispose()方法.任何包含非托管资源的类,都应该继承此接口。
在一个包含非托管资源的类中,关于资源释放的标准做法是:
- 继承IDisposeable接口;
- 事项Dispose()方法,在其中释放托管资源和非托管资源,并将对象本身从垃圾回收器中移除(垃圾回收器不在回收此资源);
- 实现类析构函数,在其中释放非托管资源
显示调用Dispose()方法,可以及时的释放资源, 如果没有显示调用Dispose()方法,垃圾回收器也可以通过析构函数来释放非托管资源,只不过由垃圾回收器回收会导致非托管资源的未能及时释放
注: 在.net中应该尽可能的少使用析构函数释放资源, 最好及时的调用Dispose()方法来回收资源,而不是依赖垃圾回收器
问题:通过Dispose进行资源的释放也是有潜在的风险的, 如果代码中漏掉了Dispose的调用或者在Dispose调用之前产生了异常从而没有指定Dispose,那么有些资源可能就一直留在内存中了
解决:可以用try/finally 来保证dispose一定会调用, 但是每个地方都这么写有些麻烦,using语句提供了一个高效的调用对象Dispose方法的方式.对于任何IDisposable接口的类型,都可以使用using语句,在using语句块结束的时候,mr实例的Dispose方法会被自动调用,事实上,C#编译器为using语句自动添加了try/finally语句块
Finalize方式
释放非托管资源除了dispose,还有重写Finalize方法,就是上面提到的第三步,为什么是析构函数呢?因为Finalize方法不能被直接重写,只能通过这个方式才能重写它。
而且最重要的原因是C#编译器会为Finalize方法隐式的加入一些必要的基础代码,这些代码就是保证finalize方法总是能被执行。
注: Finalize方法的调是相当耗费资源的,如果创建了一个不使用非托管资源的类型,实现终结器是没有任何作用的,所以,如果没有特殊的需要应该避免重写Finalize方法。
Finalize与Dispose结合使用
Dispose和Finalize的结合,保证非托管资源的回收。
public class MyResourceWrapper : IDisposable
{
private bool IsDisposed = false;
~MyResourceWrapper()
{
Dispose(false);
}
public void Dispose(bool disposing)
{
if (!this.IsDisposed)
{
if (disposing)
{
//清除托管资源
}
//清除非托管资源
}
this.IsDisposed = true;
}
public void Dispose()
{
Dispose(true);
//告诉GC不调用Finalize方法
GC.SuppressFinalize(this);
}
}
优点:
- 如果没有显式的调用Dispose(),未释放托管和非托管资源,那么在垃圾回收时,还会执行Finalize,释放非托管资源,同时GC会释放托管资源.
- 如果调用了Dispose(),就能及时释放托管和非托管资源,那么该对象被垃圾回收时,就不会执行Finalize(),提高了非托管资源的使用效率并提升了系统性能