是否真的只需要为托管资源实现处置模式?

qua*_*con 3 .net c# dispose idisposable finalizer

我已经仔细阅读了这篇文章,并且似乎清楚地指出了在所有IDisposable实施情况下都应该实施处置模式。我试图理解为什么在我的班级仅持有托管资源(即其他IDisposable成员或安全句柄)的情况下需要实现处置模式的原因。为什么我不能写

class Foo : IDisposable
{
    IDisposable boo;

    void Dispose()
    {
        boo?.Dispose();
    }
}
Run Code Online (Sandbox Code Playgroud)

如果可以肯定地知道没有非托管资源,并且没有指向Dispose终结器调用方法的信息,因为托管资源没有从终结器中释放出来?

更新:为了增加一些清晰度。讨论似乎归结为以下问题:是否需要为每个实现的基础公共非密封类实现处置模式IDisposable。但是,当没有非托管资源的基类不使用dispose模式而具有非托管资源的子类确实使用此模式时,我找不到层次结构的潜在问题:

class Foo : IDisposable
{
    IDisposable boo;

    public virtual void Dispose()
    {
        boo?.Dispose();
    }
}

// child class which holds umanaged resources and implements dispose pattern
class Bar : Foo
{
    bool disposed;
    IntPtr unmanagedResource = IntPtr.Zero;

    ~Bar()
    {
        Dispose(false);
    }

    public override void Dispose()
    {
        base.Dispose();
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposed)
            return;

        if (disposing)
        {
            // Free any other managed objects here.
            //
        }
        // close handle

        disposed = true;
    }
}

// another child class which doesn't hold unmanaged resources and merely uses Dispose 
class Far : Foo
{
    private IDisposable anotherDisposable;

    public override void Dispose()
    {
        base.Dispose();
        anotherDisposable?.Dispose();
    }
}
Run Code Online (Sandbox Code Playgroud)

更重要的是,对我来说,当实现仅对他们所知道的事情负责时,看起来更好地分离了关注点。

Dav*_*oft 5

这个

private class Foo : IDisposable
{
    IDisposable boo;

    public void Dispose()
    {
        boo?.Dispose();
    }
}
Run Code Online (Sandbox Code Playgroud)

很好。照原样

public sealed class Foo : IDisposable
{
    IDisposable boo;

    public void Dispose()
    {
        boo?.Dispose();
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我没有使用virtual Dispose方法实现上述未实现的密封基类,怎么办?

文档

因为未定义垃圾收集器在完成过程中销毁托管对象的顺序,所以使用值为false调用此Dispose重载可防止终结器尝试释放可能已经回收的托管资源。

访问已被回收的托管对象,或在其被处置后访问其属性(可能由另一个终结器访问),将导致在终结器中引发异常,这很糟糕

如果Finalize或Finalize的覆盖引发异常,并且运行时不是由覆盖默认策略的应用程序托管,则运行时将终止进程,并且不会执行活动的try / finally块或终结器。如果终结器无法释放或破坏资源,则此行为可确保过程完整性。

因此,如果您有:

   public  class Foo : IDisposable
    {
        IDisposable boo;

        public virtual void Dispose()
        {
            boo?.Dispose();
        }
    }
    public class Bar : Foo
    {
        IntPtr unmanagedResource = IntPtr.Zero;
        ~Bar()
        {
            this.Dispose();
        }

        public override void Dispose()
        {
            CloseHandle(unmanagedResource);
            base.Dispose();
        }

        void CloseHandle(IntPtr ptr)
        {
            //whatever
        }
    }
Run Code Online (Sandbox Code Playgroud)

〜Bar-> Bar.Dispose()-> base.Dispose()-> boo.Dispose()但是boo可能已被GC回收。


Ser*_*nov 0

你可能搞错了。如果您没有非托管资源,则无需实现终结器。您可以通过使用 Visual Studio 中的自动模式实现来检查它(它甚至会生成注释,说明仅当您使用非托管资源时才应取消注释终结器)。

dispose 模式仅用于访问非托管资源的对象。

如果您设计一个基类,并且某些继承类访问非托管资源,则继承类将通过重写Dispose(bool)和定义终结器来自行处理它。

本文解释了,如果不被抑制,所有终结器无论如何都会被调用。Diapose(true)如果被抑制,所有的东西都会首先被调用链释放。