GC-设计 Dispose

作者:vkvi 来源:ITPOW(原创) 日期:2008-8-25

类中的 Dispose 方法被设计来释放资源。

首先要明确释放什么?

我们要释放托管资源和非托管资源,托管资源可以被垃圾回收器自动回收,为什么我们还要手动去释放呢?原因是:虽然垃圾回收器可以自动回收,但我们无法确信垃圾回收器什么时候回收,如果所占有的资源相对计算机来说相当精贵,不管它是不是托管的,我们都应该立即释放

Finalize、Dispose()、Dispose(bool disposing)

  • Finalize 是 VB 中的内容,C# 中为析构函数;
  • Dispose() 我们自己定义的释放资源的名称,你甚至可以定义为 Eat() 这些,不过微软推荐用 Dispose()、Close() 这些名字,具体请参见 Connection 对象的 Close 和 Dispose
  • Dispose(bool disposing) 是接口 IDisposable 的一个方法,我们在类中实现时方法名和参数都不能变。

Finalize 不能人工调用,它只释放对象中的非托管资源,因为自动调用 Finalize 时,对象中的托管资源或许已经被自动回收。

Dispose() 显示地释放托管资源和非托管资源,如果调用该方法,托管资源和非托管资源将立即得到释放。

根据上述,我们可以发现:Finalize 做的工作是 Dispose() 的一部分,那么为了提高代码的复用,我们是否可以另外写一个方法,该方法可以做全部的清理工作,但也可以根据参数来只做一部分清理工作呢?对了,这就是 IDisposable 的 Dispose(bool disposing) 方法。

public class DisposableResource : IDisposable
{
    ~DisposableResource()
    {
        Dispose(false);
    }
   
   
    public void Dispose()
    {
        Dispose(true);
    }
   
   
    //实现接口 IDisposable 的 Dispose(bool disposing) 方法。
    protected void Dispose(bool disposing)
    {
        if (disposing)
        {
            //释放托管资源
        }
       
        //释放非托管资源
    }
}

可以看到,调用 Dispose() 方法后,由于 disposing 为 true,将释放托管和非托管资源,而垃圾回收器调用 Finalize(析构函数)时,将只释放非托管资源(此时托管资源在其它不确信的时候被垃圾回收器回收)。

由于 Dispose() 已经释放了所有资源,垃圾回收器还有没有必要再调用 Finalize 呢?没有必要了。所以我们要告诉垃圾回收器不需要再调用 Finalize 了,故在 Dispose() 中增加一句,代码如下:

public class DisposableResource : IDisposable
{
    ~DisposableResource()
    {
        Dispose(false);
    }
   
   
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
   
   
    //实现接口 IDisposable 的 Dispose(bool disposing) 方法。
    protected void Dispose(bool disposing)
    {
        if (disposing)
        {
            //释放托管资源
        }
       
        //释放非托管资源
    }
}

还有一个问题,如何让程序更健壮些,以避免多次调用 Dispose 时出错,我们可以增加一个字段来表明是否已经清理过资源了,若已经清理了,则不再清理,代码如下:

public class DisposableResource : IDisposable
{
    private bool _disposed = false;
   
   
    ~DisposableResource()
    {
        Dispose(false);
    }
   
   
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
   
   
    //实现接口 IDisposable 的 Dispose(bool disposing) 方法。
    protected void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                //释放托管资源
            }
           
            //释放非托管资源
           
            _disposed = true;
        }
    }
}

实际上,上述代码,就是一个标准的 Dispose 设计模板。

  • 如果我们有基类,我们还需要在 Dispose() 中调用基类的 Dispose() 方法。
  • 如果我们的类中有一个字段为 Image _img,应该在 disposing 为 true 的情况下调用 _img.Dispose(),因为 Image 是个托管资源类型,虽然它管理着非托管资源。

我们在使用类时:

  • 如果调用了 Dispose(),则托管资源和非托管资源都将被释放,事情也就此完成。
  • 如果没有调用 Dispose(),在某个时候(无法确信具体时间),垃圾回收器将调用 Finalize,Finalize 将释放非托管资源;而另一方面,也在某个时候,垃圾回收器将回收该对象的托管资源。

所以,如果类提供了 Dispose(),我们还是应该调用它:

  • 一方面让内存早点得到释放。
  • 另一方面避免 Finalize 被调用,因为调用 Finalize 开销要大些。
相关文章