一尘不染

在C#中使用Finalize / Dispose方法

c#

C#2008

我已经为此工作了一段时间,但我仍然对在代码中使用finalize和dispose方法感到困惑。我的问题如下:

  1. 我知道在处理非托管资源时我们只需要一个终结器即可。但是,如果存在调用非托管资源的托管资源,是否仍需要实现终结器?

  2. 但是,如果我开发的类不直接或间接使用任何非托管资源,是否应该实现,IDisposable以允许该类的客户端使用“使用语句”?

实现IDisposable只是为了使您的类的客户端能够使用using语句是否可行?

    using(myClass objClass = new myClass())
{
    // Do stuff here
}
  1. 我在下面开发了这个简单的代码来演示Finalize / dispose的使用:
    public class NoGateway : IDisposable
    

    {
    private WebClient wc = null;

    public NoGateway()
    {
        wc = new WebClient();
        wc.DownloadStringCompleted += wc_DownloadStringCompleted;
    }
    
    // Start the Async call to find if NoGateway is true or false
    public void NoGatewayStatus()
    {
        // Start the Async's download
            // Do other work here
        wc.DownloadStringAsync(new Uri(www.xxxx.xxx));
    }
    
    private void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
    {
        // Do work here
    }
    
    // Dispose of the NoGateway object
    public void Dispose()
    {
        wc.DownloadStringCompleted -= wc_DownloadStringCompleted;
        wc.Dispose();
        GC.SuppressFinalize(this);
    }
    

    }

有关源代码的问题:

  1. 在这里,我没有添加终结器,通常,终结器将由GC调用,而终结器将调用Dispose。由于没有终结器,我什么时候调用Dispose方法?是该类的客户必须调用它吗?

因此,在示例中,我的类称为NoGateway,客户端可以使用和处置此类,如下所示:

    using(NoGateway objNoGateway = new NoGateway())
{
    // Do stuff here   
}

当执行到达using块的末尾时,将自动调用Dispose方法,还是客户端必须手动调用dispose方法?即

    NoGateway objNoGateway = new NoGateway();
// Do stuff with object
objNoGateway.Dispose(); // finished with it
  1. 我使用的是WebClient在我的班级NoGateway课。因为WebClient实现IDisposable接口,这是否意味着WebClient间接使用非托管资源?有严格的规则可以遵循吗?我怎么知道一个类使用非托管资源?

阅读 247

收藏
2020-05-19

共1个答案

一尘不染

推荐的IDisposable模式在此处。在编写使用IDisposable的类时,通常应使用两种模式:

当实现不使用非托管资源的密封类时,只需像常规接口实现一样实现Dispose方法:

public sealed class A : IDisposable
{
    public void Dispose()
    {
        // get rid of managed resources, call Dispose on member variables...
    }
}

实现未密封的类时,请执行以下操作:

public class B : IDisposable
{    
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // get rid of managed resources
        }   
        // get rid of unmanaged resources
    }

    // only if you use unmanaged resources directly in B
    //~B()
    //{
    //    Dispose(false);
    //}
}

请注意,我尚未在中声明终结器B;仅当您有要处理的实际非托管资源时,才应实现终结器。CLR处理可终结对象与不可终结对象的方式有所不同,即使SuppressFinalize被调用也是如此。

因此,除非必须这样做,否则不应该声明终结器,但是如果类的继承者Dispose直接使用非托管资源,则可以给它们的继承者一个钩子来调用您自己并实现终结器:

public class C : B
{
    private IntPtr m_Handle;

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            // get rid of managed resources
        }
        ReleaseHandle(m_Handle);

        base.Dispose(disposing);
    }

    ~C() {
        Dispose(false);
    }
}

如果您不直接使用非托管资源(SafeHandle并且朋友不声明,因为他们声明了自己的终结器),则不要实现终结器,因为GC处理终结类的方式有所不同,即使您以后取消终结器也是如此。还要注意,即使B没有终结器,它仍然会调用SuppressFinalize以正确处理实现终结器的所有子类。

当一个类实现IDisposable接口时,这意味着当您结束使用该类时,应该删除一些非托管资源。实际资源封装在类中;您无需明确删除它们。只需Dispose()将类调用或包装在中即可using(...) {}确保在必要时清除所有不受管理的资源。

2020-05-19