为什么推荐的 dispose 模式在层次结构的每一层都添加一个 disposed 字段?

Why does the recommended dispose pattern adds a disposed field at each level of the hierarchy?

众所周知,Dispose 模式很难正确处理,尤其是当我们有一个 class 层次结构需要在不同级别处理事物时。推荐的实现如下,取自 Implement a Dispose method - Microsoft Docs.

using System;

class BaseClass : IDisposable
{
    // To detect redundant calls
    private bool _disposed = false;

    ~BaseClass() => Dispose(false);

    // Public implementation of Dispose pattern callable by consumers.
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    // Protected implementation of Dispose pattern.
    protected virtual void Dispose(bool disposing)
    {
        if (_disposed)
        {
            return;
        }

        if (disposing)
        {
            // TODO: dispose managed state (managed objects).
        }

        // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
        // TODO: set large fields to null.

        _disposed = true;
    }
}

using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices;

class DerivedClass : BaseClass
{
    // To detect redundant calls
    private bool _disposed = false;

    // Instantiate a SafeHandle instance.
    private SafeHandle _safeHandle = new SafeFileHandle(IntPtr.Zero, true);

    // Protected implementation of Dispose pattern.
    protected override void Dispose(bool disposing)
    {
        if (_disposed)
        {
            return;
        }

        if (disposing)
        {
           // Dispose managed state (managed objects).
            _safeHandle?.Dispose();
        }

        _disposed = true;

        // Call base class implementation.
        base.Dispose(disposing);
    }
}

我在此实现中没有得到的是我们在层次结构的每个级别添加 _disposed 字段有什么优势?相反,我们可以只在层次结构的顶层处理 _disposed(直接实现 IDisposable 而在派生的 classes 中不关心它。

像这样:

using System;

class BaseClass : IDisposable
{
    // To detect redundant calls
    private bool _disposed = false;

    ~BaseClass()
    {
       if (_disposed)
       {
          return;
       }

       Dispose(false);
       _disposed = true;
    }

    // Public implementation of Dispose pattern callable by consumers.
    public void Dispose()
    {
        if (_disposed)
        {
            return;
        }

        Dispose(true);
        GC.SuppressFinalize(this);
        _disposed = true;
    }

    // Protected implementation of Dispose pattern.
    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // TODO: dispose managed state (managed objects).
        }

        // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
        // TODO: set large fields to null.
    }
}

using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices;

class DerivedClass : BaseClass
{
    // Instantiate a SafeHandle instance.
    private SafeHandle _safeHandle = new SafeFileHandle(IntPtr.Zero, true);

    // Protected implementation of Dispose pattern.
    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
           // Dispose managed state (managed objects).
            _safeHandle?.Dispose();
        }

        // Call base class implementation.
        base.Dispose(disposing);
    }
}

这是一个如此广泛使用的代码示例,肯定有充分的理由在每个级别重复 _disposed 来实现它,但我找不到任何。

基础 class 中的代码稍微多了一点,但在派生 class 中不用担心,并且删除了一些重复的信息。

我还缺少什么?

编辑:

正如@InBetween 正确指出的那样,我的实现的一个缺点是如果您需要检查您的对象是否在派生 class 的一种方法中被处置,您将无法检查它.让我们通过将其设为受保护的 属性 和私有集来纠正该问题。

using System;

class BaseClass : IDisposable
{
    // To detect redundant calls
    protected bool Disposed { get; private set; } = false;

    ~BaseClass()
    {
       if (Disposed)
       {
          return;
       }

       Dispose(false);
       Disposed = true;
    }

    // Public implementation of Dispose pattern callable by consumers.
    public void Dispose()
    {
        if (Disposed)
        {
            return;
        }

        Dispose(true);
        GC.SuppressFinalize(this);
        Disposed = true;
    }

    // Protected implementation of Dispose pattern.
    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // TODO: dispose managed state (managed objects).
        }

        // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
        // TODO: set large fields to null.
    }
}

using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices;

class DerivedClass : BaseClass
{
    // Instantiate a SafeHandle instance.
    private SafeHandle _safeHandle = new SafeFileHandle(IntPtr.Zero, true);

    public void DoSomething()
    {
       if(Disposed)
       {
           throw new ObjectDisposedException("Cannot access disposed resource");
       }
    }    

    // Protected implementation of Dispose pattern.
    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
           // Dispose managed state (managed objects).
            _safeHandle?.Dispose();
        }

        // Call base class implementation.
        base.Dispose(disposing);
    }
}

原因很简单。在您的实现中,您不能在派生类型中使用 _disposed 来检查在对象已被处置时是否调用了任何方法并采取必要的操作。在您的实施中,您需要创建自己的冗余标志 isDisposed ,这违背了目的;您已经按照指南从模式本身获得了一个 "for free"。

虽然可以制作一个案例 _disposed protected.

如果您继承自一次性 class,两个条件之一必须为真。

  1. 您的 subcless 没有引入新的一次性资源。在这种情况下,您不需要覆盖 Dispose 并且问题没有实际意义。

  2. 您的子class引入了一种新的一次性资源。在这种情况下,您将覆盖 Dispose,插入您自己的处理代码,然后调用 base.Dispose_disposed 标志可以帮助您记住防止处理代码执行两次。

如果需要,您当然可以删除 _disposed。但是你可能不太关心 base class' _disposed 标志,如果它有的话。它担心它在处理什么,你担心你的。