在 C# 中使用 IDisposable 的模板方法模式

Template method pattern with IDisposable in C#

我看到 common guideline 在 C# 中实现一次性模式是有一个抽象基础 class 实现 Dispose() 方法IDisposable 接口并提供受保护的虚拟 Disposable(bool) 方法。然后 sub-classes 需要覆盖 Disposable(bool) 并且总是做类似的事情:

if (!disposed && disposing)
{
    // dispose resources here

    disposed = true;
}

我的问题是:不可能重用这个模式吗?我不喜欢必须在每个子 class 中管理这个 "disposed state"。我们可以有一些像这样的抽象基础 class:

public abstract class AbstractResource : IDisposable
{
    private bool myDisposed = false;

    protected abstract void ReleaseUnmanagedResources();

    protected virtual void ReleaseManagedResources()
    {
        // optionally override this to explicitly call Dispose() of other IDisposable objects
    }

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

    private void Dispose(bool disposing)
    {
        if (!myDisposed)
        {
            if (disposing)
            {
                ReleaseManagedResources();
            }

            ReleaseUnmanagedResources();

            myDisposed = true;
        }
    }

    ~AbstractResource()
    {
        Dispose(false);
    }
}

然后这个 class 的用户只需要实现 ReleaseUnmanagedResources 和(可选)ReleaseManagedResources。无需处理布尔值。

我发现 this article 提出了类似的技术。但就是这样!没有其他人提到它。这种方法有缺陷吗?

这种方法的最大缺陷是它占用了有限的资源...从一个资源派生的能力 class 意味着如果我使用你的实现,我基本上可以和 OOP 说再见了,因为我会需要从您的 class 中派生出许多 class 并且不能从另一个 class 中派生出它们,如果我实现了接口就可以。

所以在现实世界中,您会混合使用您的 baseclass 一些 classes,一些出于商业目的滚动他们自己的 baseclass包含 IDisposable 和一些只是实现接口。我不能代表所有人,但我宁愿使用工具(例如 R#)将 same 构造应用于所有 classes,而不是将事物混合在一起.

如果我们有能力从多个基础派生 class,这可能是一回事,但我们在 C# 中没有。

比你想象的还要糟糕。如果你有这样的 class 层次结构,你可能需要检查 每个方法中的 disposed 和 属性,因为 class对象可以在非托管资源的生命周期之后存活很长时间,以确保您永远不会访问已处置的资源。这意味着你的摘要 class 真的没有太大帮助。

没有你想的那么糟糕。您可能一开始就不应该实现终结器 (~AbstractResource()) 或完整的 Dispose 模式。

人们普遍认为 class他们需要这些实际上根本不应该拥有的东西。仅仅因为您在 class 中使用了非托管资源,这并不一定意味着 class 需要 IDisposable,如果您以按其自身类型处理资源的方式使用该资源。如果 IDisposable 控制的资源已经被另一种类型终结,那么仅仅因为你实现了 IDisposable 并不意味着你需要终结器。

这篇文章也值得您阅读:

IDisposable: What Your Mother Never Told You About Resource Deallocation