C# 典型的 IDisposable 模式在某些情况下不会导致资源泄漏吗
C# Will the typical IDisposable pattern not cause resource leak in some condition
下面是 C# class 的典型 IDisposable 实现,它同时具有托管和非托管资源。我的问题是,对于任何给定对象,是否会出现在 class 的 Dispose() 方法之前调用 ~DisposableObject() 析构函数方法的情况。如果可能的话,它是否不会将处置标志设置为 false,并且托管资源将永远没有机会被处置,因为如果处置标志设置为 false,Dispose(bool) 方法不会执行任何操作。
public class DisposableObject : IDisposable
{
private bool disposed;
private ManagedResource mgdRs;
private UnmanagedResource unMgdRs;
public DisposableObject()
{
mgdRs = new ManagedResource();
unMgdRs = new UnmanagedResource();
}
~DisposableObject()
{
this.Dispose(false);
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
// clean up managed resources
//call dispose on your member objects
mgdRs.Dispose();
}
// clean up unmanaged resources
unMgdRs.Destroy();
this.disposed = true;
}
}
}
}
是的,可能首先调用终结器。不过,这里有三点要明白。
首先,在 C# 中很少需要实现完整的 IDisposable
模式。仅当您同时拥有同一类型的托管资源和 原始 非托管资源时才需要完整模式。
您处理的大多数非托管资源已经有一个您依赖的托管包装器,因此没有必要实施另一个 finalizer/destructor。此外,实现需要终结器的原始非托管资源的开发人员通常会将自己限制在一个资源上,这样就没有相同类型的托管资源来创建问题的冲突。通过这种方式,非托管资源变为托管资源,现在可以作为更简单模式的一部分包含在内。
要了解的第二件事是终结器作为垃圾收集的一部分运行。鉴于终结器是 运行,我们知道非托管资源已清理,只保留托管资源。托管资源的定义正是垃圾收集器将采用的东西。因此,如果终结器运行得太快,导致 Dispose()
调用错过托管资源,那没关系,因为垃圾收集器已经 运行 并且会处理它。
这让我想到了我的第三件事。终结器不仅可能首先被调用,而且当这种情况发生时,终结器很可能只被调用 ,因为除了垃圾收集器之外,调用终结器是非常不寻常的。如果对象的垃圾收集器是 运行,则将不会有任何剩余引用可用于调用 Dispose(),无论是有意还是隐含。同样,没关系:终结器是 运行,所以我们知道非托管资源将被清理,垃圾收集器是 运行,因此我们知道托管资源也将被清理。
下面是 C# class 的典型 IDisposable 实现,它同时具有托管和非托管资源。我的问题是,对于任何给定对象,是否会出现在 class 的 Dispose() 方法之前调用 ~DisposableObject() 析构函数方法的情况。如果可能的话,它是否不会将处置标志设置为 false,并且托管资源将永远没有机会被处置,因为如果处置标志设置为 false,Dispose(bool) 方法不会执行任何操作。
public class DisposableObject : IDisposable
{
private bool disposed;
private ManagedResource mgdRs;
private UnmanagedResource unMgdRs;
public DisposableObject()
{
mgdRs = new ManagedResource();
unMgdRs = new UnmanagedResource();
}
~DisposableObject()
{
this.Dispose(false);
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
// clean up managed resources
//call dispose on your member objects
mgdRs.Dispose();
}
// clean up unmanaged resources
unMgdRs.Destroy();
this.disposed = true;
}
}
}
}
是的,可能首先调用终结器。不过,这里有三点要明白。
首先,在 C# 中很少需要实现完整的 IDisposable
模式。仅当您同时拥有同一类型的托管资源和 原始 非托管资源时才需要完整模式。
您处理的大多数非托管资源已经有一个您依赖的托管包装器,因此没有必要实施另一个 finalizer/destructor。此外,实现需要终结器的原始非托管资源的开发人员通常会将自己限制在一个资源上,这样就没有相同类型的托管资源来创建问题的冲突。通过这种方式,非托管资源变为托管资源,现在可以作为更简单模式的一部分包含在内。
要了解的第二件事是终结器作为垃圾收集的一部分运行。鉴于终结器是 运行,我们知道非托管资源已清理,只保留托管资源。托管资源的定义正是垃圾收集器将采用的东西。因此,如果终结器运行得太快,导致 Dispose()
调用错过托管资源,那没关系,因为垃圾收集器已经 运行 并且会处理它。
这让我想到了我的第三件事。终结器不仅可能首先被调用,而且当这种情况发生时,终结器很可能只被调用 ,因为除了垃圾收集器之外,调用终结器是非常不寻常的。如果对象的垃圾收集器是 运行,则将不会有任何剩余引用可用于调用 Dispose(),无论是有意还是隐含。同样,没关系:终结器是 运行,所以我们知道非托管资源将被清理,垃圾收集器是 运行,因此我们知道托管资源也将被清理。