在了解Finalize和Dispose之前,我们需要了解两个概念,一个是托管资源,一个非委托资源。
a.其中托管资源一般是指被CLR控制的内存资源,这些资源的管理可以由CLR来控制,例如程序中分配的对象,作用域内的变量等。
b.而非托管资源是CLR不能控制或者管理的部分,这些资源有很多,比如文件流,数据库的连接,系统的窗口句柄,打印机资源等等……这些资源一般情况下不存在于Heap(内存中用于存储对象实例的地方)中。
.Net平台中,CLR为程序员提供了一种很好的内存管理机制,使得程序员在编写代码时不需要显式的去释放自己使用的内存资源(这些在先前C和C++中是需要程序员自己去显式的释放的)。这种管理机制称为GC(garbage collection)。GC的作用是很明显的,当系统内存资源匮乏时,它就会被激发,然后自动的去释放那些没有被使用的托管资源(也就是程序员没有显式释放的对象)。
但正如上面说的,CLR的GC功能也只能释放托管资源,对于非托管资源例如窗口,文件和网络连接等,它都只能跟踪非托管资源的生存期,而不知道如何去释放它。这样就会出现当资源用尽时就不能提供资源能够提供的服务,windows的运行速度就会变慢。这样的情况会出现在数据库的连接当中,当你没有显式的释放一个数据库资源时,如果还是不断的申请数据库资源,那么到一定时候程序就会抛出一个异常。
所以,当我们在类中封装了对非托管资源的操作时,我们就需要显式,或者是隐式的释放这些资源。而上面提到的Finalize和Dispose方法分别就是隐式和显式操作中分别使用到的方法。
Finalize一般情况下用于基类不带close方法或者不带Dispose显式方法的类,也就是说,在Finalize过程中我们需要隐式的去实现非托管资源的释放,然后系统会在Finalize过程完成后,自己的去释放托管资源。
如果要实现Dispose方法,可以通过实现IDisposable接口,这样用户在使用这个类的同时就可以显示的执行Dispose方法,释放资源。
以下是MSDN上提出的Finalize和Dispose方法的使用指南,如果你的类遵循这个标准的话,你写出的类在.Net平台上就是一个“良民”。
Finalize
下面的规则概括了?Finalize?方法的使用指南。
1.仅在要求终结的对象上实现?Finalize。存在与?Finalize?方法相关的性能开销。
如果需要?Finalize?方法,应考虑实现?IDisposable,以使类的用户可以避免调用?Finalize?方法带来的开销。(juky_huang注:在实现IDisposable的类中,可以通过GC.SuppressFinalize来停止Finalize的运行,这样只要显式的调用了Dispose方法,就能给用户提供更小的开销。如果用户没有显式的调用Dispose方法,也就是没有停止Finalize的运行,这样就可以隐式的实现非托管资源的释放)
2.不要使?Finalize?方法更可见。它应该是?protected,而不是?public。?(juky_huang注:这个很重要,Finalize方法一般是系统调用,用户不去显式的调用它)
3.对象的?Finalize?方法应该释放对象拥有的任何外部资源。此外,Finalize?方法应该仅释放由对象控制的资源。Finalize?方法不应该引用任何其他对象。
4.不要对不是对象的基类的对象直接调用?Finalize?方法。在?C#?编程语言中,这不是有效的操作。
5.从对象的?Finalize?方法调用?base.Finalize?方法。(juky_huang注:就是派生类调用基类的Finalize方法)
注意???基类的?Finalize?方法由?C#?和?C++?的托管扩展的析构函数语法自动调用。
Dispose
下面的规则概括了?Dispose?方法的使用指南:
1.在封装明确需要释放的资源的类型上实现处置设计方案。用户可以通过调用公共?Dispose?方法释放外部资源。
2.在通常包含控制资源的派生类型的基类型上实现处置设计方案,即使基类型并不需要。如果基类型有?close?方法,这通常指示需要实现?Dispose。在这类情况下,不要在基类型上实现?Finalize?方法。应该在任何引入需要清理的资源的派生类型中实现?Finalize。
3.使用类型的?Dispose?方法释放类型所拥有的任何可处置资源。
4.对实例调用了?Dispose?后,禁止?Finalize?方法通过调用?GC.SuppressFinalize?方法运行。此规则的例外情况是当必须用?Finalize?完成?Dispose?没有覆盖的工作时,但这种情况很少见。
5.如果基类实现?IDisposable,则调用基类的?Dispose?方法。
6.不要假定?Dispose?将被调用。如果?Dispose?未被调用,也应该使用?Finalize?方法释放类型所拥有的非托管资源。
7.处置了资源之后,在该类型(非?Dispose)上从实例方法引发一个?ObjectDisposedException。该规则不适用于?Dispose?方法,因为在不引发异常的情况下,该方法应该可以被多次调用。
8.通过基类型的层次结构将调用传播到?Dispose。Dispose?方法应释放此对象控制的所有资源和此对象所拥有的任何对象。例如,可以创建一个类似?TextReader?的对象来控制?Stream?和?Encoding,两者均在用户不知道的情况下由?TextReader?创建。另外,Stream?和?Encoding?都可以获取外部资源。当对?TextReader?调用Dispose方法时,它应该依次对?Stream?和?Encoding?调用?Dispose,使它们释放它们的外部资源。
9.应考虑在调用了对象的?Dispose?方法后不允许使用对象。重新创建已处置的对象是难以实现的方案。
10.允许?Dispose?方法被调用多次而不引发异常。此方法在首次调用后应该什么也不做。
有了以上的基础后,我们看一段代码,这段代码是Dispose的一个实现,这个代码如果仔细的去考虑的话,非常的有趣,在这里我们又会看到C#中一个非常常用的技术,多态性,如果你看过我在前面写的一篇关于虚拟方法的文章的话,你可以从中理解下面代码的精要之处。
public class BaseResource: IDisp