Microsoft IDisposable 模式实际上正确吗?

fum*_*hez 6 c# idisposable

我多次偶然发现 Microsoft 推荐的实现 IDisposable 模式的方法,它甚至在 Visual Studio 中作为灯图标菜单中的“实现接口”选项出现。它看起来像这样:

// Override only if 'Dispose(bool disposing)' has code to free unmanaged resources
~Foo() {
    // Do not change this code.
    Dispose(calledByFinalizer: true);
}
Run Code Online (Sandbox Code Playgroud)
public void Dispose() {
    // Do not change this code. 
    Dispose(calledByFinalizer: false);
    GC.SuppressFinalize(this);
}
Run Code Online (Sandbox Code Playgroud)
// Put cleanup code here
protected virtual void Dispose(bool calledByFinalizer) {
    if (_disposed) return;

    if (!calledByFinalizer) { /* dispose managed objects */ }

    /* free unmanaged resources and set large fields to null */

    _disposed = true;
}
Run Code Online (Sandbox Code Playgroud)

我稍微重构了建议的代码(因为 Dispose(bool dissetting) 可能会伤人的大脑,而嵌套的 if 会伤人的眼睛)。

但我心中仍然有一些疑问:

  1. 假设该方法将被调用一次。那为什么要_disposed = true放在方法的末尾而不是开头呢?如果IDisposable.Dispose()从不同的线程调用,那么它们都可以绕过检查if (_disposed) return;并实际执行方法体两次。为什么不这样做:
    if (_disposed) return;
    else _disposed = true;
Run Code Online (Sandbox Code Playgroud)
  1. 为什么被protected virtual void Dispose(bool disposing)标记为virtual?任何派生类都无权访问该_disposed字段,并且很容易破坏其行为。我们只能将其标记为virtual可选部分,派生类可以在不调用的情况下执行任何操作base.Dispose()
~Foo() => FreeUnmanagedResources();

public void Dispose() {
    if (_disposed) return;
    else _disposed = true;

    DisposeManagedObjects();
    FreeUnmanagedResources();

    GC.SuppressFinalize(this);
}

protected virtual void DisposeManagedObjects() { }
protected virtual void FreeUnmanagedResources() { }
Run Code Online (Sandbox Code Playgroud)

Joe*_*orn 7

该模式是正确的,但假设最坏的情况,您还必须实现终结器。也就是说,如果您需要终结器,您还必须遵循整个模式。然而...

\n

很少需要终结器。

\n

仅当您要为全新类型的非托管资源创建原始托管包装器时,才需要终结器。

\n

例如,假设您创建了一个全新的、以前从未见过的数据库系统。您希望为这种新型数据库提供 .Net ADO 提供程序,包括连接(它将继承自 DbConnection)。这里的底层网络操作将是非托管资源,并且还没有终结器可以在继承树中的任何位置释放它们。因此,您必须实现自己的终结器。

\n

另一方面,如果您正在为应用程序创建一个包装器对象来管理与现有数据库类型 \xe2\x80\x94 的连接,只需重新打包(包装或继承)现有的 SqlConnection、OleDbConnection、MySqlConnection 等 \xe2\x80\ x94 那么你仍然应该实现 IDisposable,但是已经为非托管资源提供了一个终结器,你不需要编写另一个。

\n

事实证明,当您没有终结器时,您可以安全地从记录的 IDisposable 模式中删除大量代码。

\n

从微软的角度来看,最好鼓励人们添加一些额外的无害代码行,而不是让他们错过一些可能重要的东西。

\n