一:总结
1、Finalize
方法(C#中是析构函数,以下称析构函数)是用于释放非托管资源的,而托管资源会由GC自动回收。所以,我们也可以这样来区分 托管和非托管资源。所有会由GC自动回收的资源,就是托管的资源,而不能由GC自动回收的资源,就是非托管资源。在我们的类中直接使用非托管资源的情况很 少,所以基本上不用我们写析构函数。
2、大部分的非托管资源会给系统带来很多负面影响,例如数据库连接不被释放就可能导致连接池中的可用数据库连接用尽。文件不关闭会导致其它进程无法读写这个文件等等。
实现模型:
1、由于大多数的非托管资源都要求可以手动释放,所以,我们应该专门为释放非托管资源公开一个方法。实现IDispose
接口的Dispose
方法是最好的模型,因为C#支持using
语句快,可以在离开语句块时自动调用Dispose
方法。
2、虽然可以手动释放非托管资源,我们仍然要在析构函数中释放非托管资源,这样才是安全的应用程序。否则如果因为程序员的疏忽忘记了手动释放非托管资源, 那么就会带来灾难性的后果。所以说在析构函数中释放非托管资源,是一种补救的措施,至少对于大多数类来说是如此。
3、由于析构函数的调用将导致GC
对对象回收的效率降低,所以如果已经完成了析构函数该干的事情(例如释放非托管资源),就应当使用SuppressFinalize
方法告诉GC
不需要再执行某个对象的析构函数。
4、析构函数中只能释放非托管资源而不能对任何托管的对象/资源进行操作。因为你无法预测析构函数的运行时机,所以,当析构函数被执行的时候,也许你进行操作的托管资源已经被释放了。这样将导致严重的后果。
5、(这是一个规则)如果一个类拥有一个实现了IDispose
接口类型的成员,并创建(注意是创建,而不是接收,必须是由类自己创建)它的实例对象,则 这个类也应该实现IDispose
接口,并在Dispose
方法中调用所有实现了IDispose
接口的成员的Dispose
方法。
只有这样的才能保证所有实现了IDispose
接口的类的对象的Dispose
方法能够被调用到,确保可以手动释放任何需要释放的资源。
二:实现 Finalize
和 Dispose
以清理非托管资源
http://msdn.microsoft.com/zh-cn/library/b1yfkh5e(VS.80).aspx
三:维护内部非托管资源的托管类的手段:Finalize()
–终结和Dispose()
–处置
非托管资源:原始的操作系统文件句柄,原始的非托管数据库连接,非托管内存或其他非托管资源。
Finalize()
特性:
- 重写
Finalize()
的唯一原因是,c#类通过PInvoke
或复杂的COM
互操作性任务使用了非托管资源(典型的情况是通过System.Runtime.InteropServices.Marshal
类型定义的各成员)注:PInvoke
是平台调用服务。 object
中有finalize
方法,但创建的类不能重写此方法,若Overide
会报错,只能通过析构函数来达到同样的效果。Finalize
方法的作用是保证.NET
对象能在垃圾回收时清除非托管资源。- 在
CLR
在托管堆上分配对象时,运行库自动确定该对象是否提供一个自定义的Finalize
方法。如果是这样,对象会被标记为可终结的,同时一个指向这个对象的指针被保存在名为终结队列的内部队列中。终结队列是一个由垃圾回收器维护的表,它指向每一个在从堆上删除之前必须被终结的对象。 - 注意:
Finalize
虽然看似手动清除非托管资源,其实还是由垃圾回收器维护,它的最大作用是确保非托管资源一定被释放。 - 在结构上重写
Finalize
是不合法的,因为结构是值类型,不在堆上,Finalize
是垃圾回收器调用来清理托管堆的,而结构不在堆上。
Dispose()
特性:
- 为了更快更具操作性进行释放,而非让垃圾回收器(即不可预知)来进行,可以使用
Dispose
,即实现IDispose
接口. - 结构和类类型都可以实现
IDispose
(与重写Finalize
不同,Finalize
只适用于类类型),因为不是垃圾回收器来调用Dispose
方法,而是对象本身释放非托管资源,如Car.Dispose()
.如果编码时没有调用Dispose
方法,以为着非托管资源永远得不到释放。 - 如果对象支持
IDisposable
,总是要对任何直接创建的对象调用Dispose()
,即有实现IDisposable
接口的类对象都必须调用Dispose
方法。应该认为,如果类设计者选择支持Dispose
方法,这个类型就需要执行清除工作。记住一点,如果类型实现了IDisposable
接口,调用Dispose
方法总是正确的。 .net
基类库中许多类型都实现IDisposable
接口,并使用了Dispose
的别名,其中一个别名如IO
中的Close
方法,等等别名。使得看起来更自然。using
关键字,实际内部也是实现IDisposable
方法,用ildasm.exe
查看使用了using
的代码的CIL
,会发现是用try/finally
去包含using
中的代码,并且在finally
中调用dispose
方法。
个人总结:
相同点:
都是为了确保非托管资源得到释放。
不同点:
finalize
由垃圾回收器调用;dispose
由对象调用。finalize
无需担心因为没有调用finalize
而使非托管资源得不到释放,而dispose
必须手动调用。finalize
虽然无需担心因为没有调用finalize
而使非托管资源得不到释放,但因为由垃圾回收器管理,不能保证立即释放非托管资源;而dispose
一调用便释放非托管资源。- 只有类类型才能重写
finalize
,而结构不能;类和结构都能实现IDispose
.原因请看Finalize()
特性。