析构函数和Dispose的使用区别

老生常谈的问题了,MSDN也有很是详细的说明但看起来不是很系统。也曾经作过度析,但没有总结下来又忘了,此次整理一下MSDN和网上搜集的一些资料,以备不时只需。程序员

 

下面是MSDN对这两个函数的建议使用方法数据库

 1     // Design pattern for a base class.
 2     public class Base : IDisposable
 3     {
 4         //保证重复释放资源时系统异常
 5         private bool _isDisposed = false;
 6 
 7         // 析构函数,编译器自动生成Finalize()函数由GC自动调用,保证资源被回收。
 8         // 最好不要声明空析构函数,形成性能问题
 9         // 若是没有引用非托管资源就不须要显示声明析构函数,会形成性能问题,系统会自动生成默认析构函数
10         ~Base()
11         {
12             // 此处只须要释放非托管代码便可,由于GC调用时该对象资源可能还不须要释放
13             Dispose(false);
14         }
15 
16         //外部手动调用或者在using中自动调用,同时释放托管资源和非托管资源
17         public void Dispose()
18         {
19             Dispose(true);
20             GC.SuppressFinalize(this); ///告诉GC不须要再次调用
21         }
22 
23         protected virtual void Dispose(bool disposing)
24         {
25             if (!_isDisposed)
26             {
27                 if (disposing)
28                 {
29                     //释放托管资源
30                 }
31                 // 释放非托管资源
32                 // 释放大对象
33 
34                 this._isDisposed = true;
35             }
36            
37         }
38 
39     }

 

下面是经过Reflector工具对上面代码反射出来的结果,能够看出析构函数直接被翻译成Finalize()函数了,由于Finalize函数不能被重写,因此只能用析构函数的方式实现Finalize方法。编程

 1 public class Base : IDisposable
 2 {
 3     // Fields
 4     private bool _isDisposed;
 5 
 6     // Methods
 7     public Base();
 8     public void Dispose();
 9     protected virtual void Dispose(bool disposing);
10     protected override void Finalize(); 
11 }

 

在.NET的对象中实际上有两个用于释放资源的函数:Dispose和Finalize。Finalize的目的是用于释放非托管的资源,而Dispose是用于释放全部资源,包括托管的和非托管的。安全

 

在这个模式中,void Dispose(bool disposing)函数经过一个disposing参数来区别当前是不是被Dispose()调用。若是是被Dispose()调用,那么须要同时释放 托管和非托管的资源。若是是被~Base()(也就是C#的Finalize())调用了,那么只须要释放非托管的资源便可。编程语言

 

这是由于,Dispose()函数是被其它代码显式调用并要求释放资源的,而Finalize是被GC调用的。在GC调用的时候Base所引用的其它托管对象可能还不须要被销毁,而且即便要销毁,也会由GC来调用。所以在Finalize中只须要释放非托管资源便可。另一方面,因为在 Dispose()中已经释放了托管和非托管的资源,所以在对象被GC回收时再次调用Finalize是没有必要的,因此在Dispose()中调用 GC.SuppressFinalize(this)避免重复调用Finalize。ide

 

然而,即便重复调用Finalize和Dispose也是不存在问题的,由于有变量_isDisposed的存在,资源只会被释放一次,多余的调用会被忽略过去。所以,上面的模式保证了:函数

一、 Finalize只释放非托管资源;工具

二、 Dispose释放托管和非托管资源;性能

三、 重复调用Finalize和Dispose是没有问题的;this

四、 Finalize和Dispose共享相同的资源释放策略,所以他们之间也是没有冲突的。

 

微软对Dispose和Finalize方法使用准则


Finalize

下面的规则归纳了 Finalize 方法的使用准则:

 

       一、不能在结构中定义析构函数。只能对类使用析构函数。

       二、一个类只能有一个析构函数。

       三、没法继承或重载析构函数。

       四、没法调用析构函数。它们是被自动调用的。

       五、析构函数既没有修饰符,也没有参数。

 

  • 仅在要求终结的对象上实现 Finalize。存在与 Finalize 方法相关的性能开销。

 

  • 若是须要 Finalize 方法,应考虑实现 IDisposable,以使类的用户能够避免因调用 Finalize 方法而带来的开销。

 

  • 不要提升 Finalize 方法的可见性。该方法的可见性应该是 protected,而不是 public。

 

  • 对象的 Finalize 方法应该释放该对象拥有的全部外部资源。此外,Finalize 方法应该仅释放由该对象控制的资源。Finalize 方法不该该引用任何其余对象。

 

  • 不要对不是对象的基类的对象直接调用 Finalize 方法。在 C# 编程语言中,这不是有效的操做。

 

  • 应在对象的 Finalize 方法中调用基类的 Finalize 方法。

    注意

    基类的 Finalize 方法经过 C# 和 C++ 析构函数语法自动进行调用。

 

释放

下面的规则归纳了 Dispose 方法的使用准则:

 

  • 在封装明确须要释放的资源的类型上实现释放设计方案。用户能够经过调用公共 Dispose 方法释放外部资源。

 

  • 在一般包含控制资源的派生类型的基类型上实现释放设计方案,即便基类型并不须要也如此。若是基类型有 Close 方法,这一般指示须要实现 Dispose。在这类状况下,不要在基类型上实现 Finalize 方法。应该在任何引入须要清理的资源的派生类型中实现 Finalize。

 

  • 使用类型的 Dispose 方法释放该类型所拥有的全部可释放资源。

 

  • 对实例调用了 Dispose 后,应经过调用 GC.SuppressFinalize 方法禁止 Finalize 方法运行。此规则的一个例外是当必须用 Finalize 完成 Dispose 没有完成的工做的状况,但这种状况不多见。

 

  • 若是基类实现了 IDisposable,则应调用基类的 Dispose 方法。

 

  • 不要假定 Dispose 将被调用。若是 Dispose 未被调用,也应该使用 Finalize 方法释放类型所拥有的非托管资源。

 

  • 当资源已经释放时,在该类型上从实例方法(非 Dispose)引起一个 ObjectDisposedException。该规则不适用于 Dispose 方法,该方法应该能够在不引起异常的状况下被屡次调用。

 

  • 经过基类型的层次结构传播对 Dispose 的调用。Dispose 方法应释放由此对象以及此对象所拥有的任何对象所控制的全部资源。例如,能够建立一个相似 TextReader 的对象来控制 Stream 和 Encoding,二者均在用户不知道的状况下由 TextReader 建立。另外,Stream 和 Encoding 均可以获取外部资源。当对 TextReader 调用 Dispose 方法时,TextReader 应继而对 Stream 和 Encoding 调用 Dispose,使它们释放其外部资源。

 

  • 考虑在调用了某对象的 Dispose 方法后禁止对该对象的使用。从新建立已释放的对象是难以实现的方案。

 

  • 容许 Dispose 方法被调用屡次而不引起异常。此方法在首次调用后应该什么也不作。

 

 

下面是CSDN高手总结


一、Finalize方法(C#中是析构函数,如下称析构函数)是用于释放非托管资源的,而托管资源会由GC自动回收。因此,咱们也能够这样来区分 托管和非托管资源。全部会由GC自动回收的资源,就是托管的资源,而不能由GC自动回收的资源,就是非托管资源。在咱们的类中直接使用非托管资源的状况很 少,因此基本上不用咱们写析构函数。

 

二、大部分的非托管资源会给系统带来不少负面影响,例如数据库链接不被释放就可能致使链接池中的可用数据库链接用尽。文件不关闭会致使其它进程没法读写这个文件等等。

 

实现模型:

一、因为大多数的非托管资源都要求能够手动释放,因此,咱们应该专门为释放非托管资源公开一个方法。实现IDispose接口的Dispose方法是最好的模型,由于C#支持using语句快,能够在离开语句块时自动调用Dispose方法。

 

二、虽然能够手动释放非托管资源,咱们仍然要在析构函数中释放非托管资源,这样才是安全的应用程序。不然若是由于程序员的疏忽忘记了手动释放非托管资源, 那么就会带来灾难性的后果。因此说在析构函数中释放非托管资源,是一种补救的措施,至少对于大多数类来讲是如此。

 

三、因为析构函数的调用将致使GC对对象回收的效率下降,因此若是已经完成了析构函数该干的事情(例如释放非托管资源),就应当使用SuppressFinalize方法告诉GC不须要再执行某个对象的析构函数。

 

四、析构函数中只能释放非托管资源而不能对任何托管的对象/资源进行操做。由于你没法预测析构函数的运行时机,因此,当析构函数被执行的时候,也许你进行操做的托管资源已经被释放了。这样将致使严重的后果。

 

五、(这是一个规则)若是一个类拥有一个实现了IDispose接口类型的成员,并建立(注意是建立,而不是接收,必须是由类本身建立)它的实例对象,则 这个类也应该实现IDispose接口,并在Dispose方法中调用全部实现了IDispose接口的成员的Dispose方法。

只有这样的才能保证全部实现了IDispose接口的类的对象的Dispose方法可以被调用到,确保能够手动释听任何须要释放的资源。

相关文章
相关标签/搜索