我看到在C#中实现一次性模式的common guideline是一个抽象基类,它从 IDisposable 接口实现 Dispose()方法并提供受保护的虚拟 Disposable(bool)方法。然后子类需要覆盖 Disposable(bool)并始终执行以下操作:
if (!disposed && disposing)
{
// dispose resources here
disposed = true;
}
我的问题是:重用这种模式是不可能的?我不喜欢在每个子类中管理这个“处置状态”。我们可以有一些这样的抽象基类:
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);
}
}
然后,此类用户只需要实现 ReleaseUnmanagedResources 和(可选) ReleaseManagedResources 。无需处理布尔值。
我发现this article提出了类似的技巧。但就是这样!没人提到它。这种方法有缺陷吗?
答案 0 :(得分:4)
这种方法的最大缺陷是它占用了有限的资源......从一个类派生的能力意味着如果我使用你的实现,我基本上可以亲吻OOP再见,因为我需要从中派生出许多类你的类,如果我实现了接口,就无法从其他类派生它们。
所以在现实世界中,你会使用你的基类混合使用一些类,有些基于业务目的滚动它们自己的基类已经包含了IDisposable
,有些只是实现了接口。我不能代表所有人,但我宁愿使用工具(例如R#)将相同的构造应用于所有类,而不是混合使用。
如果我们有能力从多个基类派生,那可能是一件事,但我们不在C#中。
答案 1 :(得分:3)
这比你想象的要糟糕。如果你有这样的类heirarchy,你可能需要检查每个方法和属性中的disposed
,因为类对象可以在非托管资源的生命周期中存活很长时间,以确保你永远不会访问已处置的资源。这意味着你的抽象类实际上没有多大帮助。
它没有您想象的那么糟糕。您可能不应该首先实现终结器(~AbstractResource()
)或完整的Dispose模式。
通常认为课程需要这些,实际上根本不应该有这些课程。仅仅因为你在一个类中使用了非托管资源,它并不一定意味着类需要IDisposable
,如果你以一种自己的类型处理它的方式使用资源。仅仅因为你实现IDisposable
并不意味着你需要一个终结器,如果由IDisposable
控制的资源已经被另一种类型完成了。
这篇文章也值得你花时间:
IDisposable: What Your Mother Never Told You About Resource Deallocation