我永远不会记住实现IDisposable接口的所有规则,因此我尝试提出一个基类来处理所有这些并使IDisposable易于实现。我只是想听听你的意见,如果这个实现是好的,或者你是否看到我可以改进的东西。该基类的用户应该从中派生,然后实现两个抽象方法ReleaseManagedResources()
和ReleaseUnmanagedResources()
。所以这是代码:
public abstract class Disposable : IDisposable
{
private bool _isDisposed;
private readonly object _disposeLock = new object();
/// <summary>
/// called by users of this class to free managed and unmanaged resources
/// </summary>
public void Dispose() {
DisposeManagedAndUnmanagedResources();
}
/// <summary>
/// finalizer is called by garbage collector to free unmanaged resources
/// </summary>
~Disposable() { //finalizer of derived class will automatically call it's base finalizer
DisposeUnmanagedResources();
}
private void DisposeManagedAndUnmanagedResources() {
lock (_disposeLock) //make thread-safe
if (!_isDisposed) { //make sure only called once
try { //suppress exceptions
ReleaseManagedResources();
ReleaseUnmanagedResources();
}
finally {
GC.SuppressFinalize(this); //remove from finalization queue since cleaup already done, so it's not necessary the garbage collector to call Finalize() anymore
_isDisposed = true;
}
}
}
private void DisposeUnmanagedResources() {
lock (_disposeLock) //make thread-safe since at least the finalizer runs in a different thread
if (!_isDisposed) { //make sure only called once
try { //suppress exceptions
ReleaseUnmanagedResources();
}
finally {
_isDisposed = true;
}
}
}
protected abstract void ReleaseManagedResources();
protected abstract void ReleaseUnmanagedResources();
}
答案 0 :(得分:12)
我无法真正评论您的代码的正确性,但我怀疑您是否会在继承树的基础上找到具有灵活性的Disposable类。当你想继承自己不拥有的东西时,它没什么用处。我认为IDispoasble是一个接口,因为可以使用它的许多不同的域。在继承层次结构的底部提供具体实现将是......限制。
答案 1 :(得分:2)
编辑是的,我误读了关于分离处理托管和非托管资源的部分(仍在第一杯咖啡上)。
然而锁几乎肯定不是必需的。是的,在被动处理期间,代码将在终结器线程上运行,该线程与最初运行的线程不同。但是,如果以这种方式最终确定对象,则CLR已经确定没有对该对象的现有引用并因此收集它。因此,当时没有其他地方可以召唤您的处置,因此没有理由锁定。
其他一些风格评论。
为什么要使方法抽象化?通过这样做,您强制派生类实现处理托管和非托管资源的方法,即使它们没有任何可处置的资源。如果你没有任何东西需要处理,那么从这个课程中得出来是没有意义的。但我认为只有一个或另一个但不是两者都相当普遍。我会让他们虚拟与抽象。
此外,您可以防止双重处置,但您不会警告开发人员他们是双重处置对象。我意识到MSDN文档说双重处理应该基本上是一个无操作但同时在什么情况下这应该是合法的?一般来说,在处理完对象后访问它是个坏主意。双重处置需要重新使用被处置的对象并且可能是一个错误(如果在活动处置中没有抑制终止,这可能发生,但这也是一个坏主意)。如果我实现这个,我会抛出双重处理来警告开发人员他们正在使用一个不正确的对象(在它已经被处理后使用它)。
答案 2 :(得分:2)
您正在终结器中访问托管对象_disposeLock。到那时它可能已经被垃圾收集了。不确定这会产生什么影响,因为你只是用它来锁定。
线程安全似乎有点矫枉过正。我认为您不必担心GC线程与应用程序线程之间的争用。
否则看起来是正确的。
答案 3 :(得分:0)
如果您担心线程安全,那么我会使用轻量级联锁操作而不是重量级锁:
class MyClass: IDispose {
int disposed = 0;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public virtual void Dispose(bool disposing)
{
if (0 == Thread.InterlockedCompareExchange(ref disposed, 1, 0))
{
if (disposing)
{
// release managed resources here
}
// release unmanaged resources here
}
}
~MyClass()
{
Dispose(false);
}
}