代码改变世界

《你必须知道的.NET》读后小结之非托管资源清理

2012-03-14 20:42  Oliver_Zhao  阅读(174)  评论(0编辑  收藏  举报

   这篇文章,主要在于教大家如何写Finalize和Dispose方法。记得以前在写代码的时候,看到别人在写Dispose和析构函数,完全不知道从何下手。于是自己仔细阅读了一下,终于了然了。自用,谢绝转载。

一、终止化操作

View Code
1     class GCApp:Object 2     { 3         ~GCApp() 4         { 5         } 6     }

  将上述代码编译后,使用IL查看,可以发现,其实这个析构函数执行了一个try{}finally{}的语句块,而在finally中,调用了父类Object.Finalize()方法。.Net2.0开始,C# 编译器认为Finalize方法是一个特殊的方法,对其调用或重写必须使用析构函数语法来实现,不能通过显示覆写Finalize方法来实现。总结下来,只需要实现析构函数即可。

  在具体操作上,如果一个类型没有重写基类的Finalize方法,GC会认为该类型及其父类不需要执行终止化操作,即使当对象不可达时,也不会执行资源清理操作。如果父类重写了Finalize方法,则父类会执行终止化操作。GC.SuppressFinalize方法可以免除终结。

缺点:

  • 终止化操作的时间无法控制,执行顺序也不能保证。在资源的清理上不够灵活,也可能由于执行顺序的不确定而访问已执行了清理的对象。
  • Finalize方法会极大地损伤性能。
  • 重写了Finalize方法的类型对象,其引用类型的对象的代龄将被提升,从而带来内存压力。
  • Finalize方法在某些情况下可能不执行。应该确保重写的Finalize方法尽快被执行。

二、Dispose模式

View Code
 1     class MyDispose : IDisposable
2 {
3 private IntPtr _handle;
4 private bool disposed = false;
5
6 public void Dispose()
7 {
8 Dispose(true);
9
10 GC.SuppressFinalize(this);
11 }
12
13 protected virtual void Dispose(bool disposing)
14 {
15 if (!disposed)
16 {
17 if (disposing)
18 {
19 // 清理托管资源。
20 }
21
22 if (_handle != IntPtr.Zero)
23 {
24 CloseHandle(_handle);
25 _handle = IntPtr.Zero;
26 }
27 }
28 disposed = true;
29 }
30
31 public void Close()
32 {
33 Dispose();
34 }
35 }

在上述实现Dispose模式的典型操作中,有几点说明:

  • Dispose方法中,应该使用GC.SuppressFinalize防止GC调用Finalize方法,因为显示调用Dispose显然是较佳选择。
  • 公有Dispose方法不能实现为虚方法,以禁止在派生类中重写。
  • 在该模式中,公有Dispose方法通过调用重载虚方法Dispose(bool disposing)方法来实现,具体的资源清理操作实现与虚方法中。两种策略的区别是:disposing参数为true时,Dispose方法由用户代码调用,可释放托管或者非托管资源;disposing参数为false时,Dispose方法由Finalize调用,并且只能释放非托管资源。
  • disposed字段,保证两次调用Dispose方法不会抛异常,值得推荐。
  • 派生类中实现Dispose模式,应该重写基类的保护Dispose方法,并通过base调用基类的Dispose方法,以确保释放继承链上所有对象的引用资源。
  • 基于编程习惯,一般在实现Dispose方法时,会附加写一个Close方法来达到同样的资源清理目的,而Close其实也是通过调用Dispose来实现的。

Using语句

using语句简化了资源清理代码实现,并且能够确保Dispose方法得到调用。凡是实现了Dispose模式的类型,均可以using语句来定义其引用范围。 

View Code
public static void Main()
{
using(FileDealer fd = new FileDealer(new IntrPtr(), new ManagedRes()))
{
fd.Read();
}
}

自用,谢绝转载。谢谢!