为什么建议的处置模式会在层次结构的每个级别添加已处置字段?



众所周知,Dispose模式非常复杂,尤其是当我们有一个需要在不同级别处理事物的类层次结构时。建议的实现如下所示,取自实现处置方法 - Microsoft 文档。

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字段有什么优势?相反,我们只能在层次结构的顶层(直接实现IDisposable的层次结构(中处理_disposed,而不关心派生类中的。

像这样:

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,但我找不到任何代码示例。

基类中的代码多一点,但在派生类中不必担心,并且删除了一些重复的信息。

我还错过了什么?

编辑:

正如@InBetween正确所说,我的实现的一个缺点是,如果您需要检查您的对象是否在派生类的一种方法中释放,您将无法检查它。让我们通过使其成为具有私有集的受保护属性来更正此问题。

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);
}
}

如果从一次性类继承,则必须满足以下两个条件之一。

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

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

如果需要,您当然可以删除_disposed。但是你可能不太关心基类的_disposed标志,如果它甚至有一个的话。它担心它在处理什么,而你担心你的。

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

虽然可以提出一个关于制作_disposedprotected的理由。

最新更新