重构实现IDisposable的全局对象

35 浏览
0 Comments

重构实现IDisposable的全局对象

C# 2008

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

  1. 我知道只有在处理非托管资源时才需要使用终结器。但是,如果有托管资源调用非托管资源,是否仍需要实现终结器?
  2. 如果我开发的类不使用任何非托管资源(直接或间接),是否应该实现IDisposable以允许该类的客户端使用“using语句”?

    是否可行实现IDisposable仅使您类的客户端可以使用using语句?

    using(myClass objClass = new myClass())
    {
        // Do stuff here
    }
    

  3. 我编写了以下简单代码以说明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
    

  2. 我在我的NoGateway类中使用WebClient类。 因为WebClient实现了IDisposable接口,这是否意味着WebClient间接使用非托管资源?是否有硬性规定要遵循?如何知道一个类是否使用非托管资源?
admin 更改状态以发布 2023年5月25日
0
0 Comments

实现IDisposable的官方模式很难理解。我相信这个better更好:

public class BetterDisposableClass : IDisposable {
  public void Dispose() {
    CleanUpManagedResources();
    CleanUpNativeResources();
    GC.SuppressFinalize(this);
  }
  protected virtual void CleanUpManagedResources() { 
    // ...
  }
  protected virtual void CleanUpNativeResources() {
    // ...
  }
  ~BetterDisposableClass() {
    CleanUpNativeResources();
  }
}

一个更好的解决方案是,对于任何需要处理的非托管资源,您总是必须创建一个包装类:

public class NativeDisposable : IDisposable {
  public void Dispose() {
    CleanUpNativeResource();
    GC.SuppressFinalize(this);
  }
  protected virtual void CleanUpNativeResource() {
    // ...
  }
  ~NativeDisposable() {
    CleanUpNativeResource();
  }
}

使用SafeHandle及其派生类,这些类应该非常少见。

对于不直接处理非托管资源的可处理类,即使存在继承,结果也非常强大:它们不再需要关注非托管资源。它们将变得简单易懂:

public class ManagedDisposable : IDisposable {
  public virtual void Dispose() {
    // dispose of managed resources
  }
}

0
0 Comments

推荐使用的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中没有声明终结器;只有在实际需要处理非托管资源时才实现终结器。即使调用了SuppressFinalize,CLR也会以不同的方式处理可终结对象和不可终结对象。

因此,除非必须这样做,否则不应声明终结器,但是给类的继承者提供一个使用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(...) {}中即可确保任何非托管资源被适当地删除。

0