换句话说,
class Foo
{
object obj;
Foo() { obj = new object(); }
~Foo() { obj.ToString(); /* NullReferenceException? */ }
}
不保证两个对象的终结器以任何特定顺序运行,即使一个对象引用另一个对象。也就是说,如果对象 A 具有对对象 B 的引用并且两者都有终结器,那么当对象 A 的终结器启动时,对象 B 可能已经终结。
简而言之,您不能在终结器期间对引用对象的状态做出任何假设。
在几乎所有情况下,终结器中实现的逻辑都属于 Disposable 模式。这是如何使用IDisposable接口在 .NET 中正确实现模式的示例。
public class MyClass : IDisposable
{
private bool _disposed;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if(disposing)
{
// Release unmanaged resources.
}
// Release managed resources (Streams, SqlConnections, etc.)
}
_disposed = true;
}
}
万一您正在使用非托管资源,请查看本文以了解如何IDisposable
使用终结器实现:
这是不安全的,因为 obj 可能已经被垃圾收集了。另请注意,垃圾收集器不会将引用设置为 null。因此,即使检查 obj != null 也无济于事。
有关详细信息,请参见此处:http: //msdn.microsoft.com/en-us/magazine/cc163392.aspx#S3
"概括这个原则,在 Dispose 方法中,清理对象持有的所有资源是安全的,无论它们是托管对象还是本机资源。但是,在终结器中,只有清理不可终结的对象才是安全的,通常终结器应该只释放本机资源。 ”(你的 obj 是可终结的,所以你不应该在另一个终结器中触摸它)
这也是你拥有的原因
如果(处置){...}
在 IDisposable 模式中(参见上面链接中的图 2)。
当一个对象注册终结时,它被放置在终结队列中。当垃圾收集器运行时,所有对象分为三类:
第三种类型的对象将简单地不复存在,尽管什么都不会注意到。第一类被认为是“活的”。那些中间类型(包括具有终结器的对象,以及可终结对象持有引用的其他对象)将运行它们的终结器(如果有的话);一旦终结器完成,除非终结器将根引用存储在某个地方或重新注册对象以进行终结,否则对象将不再具有任何根引用,并且将有资格进行下一次垃圾回收。
在终结期间使用对其他对象的引用的唯一真正危险是,除非对象具有合适的互锁,否则无法知道终结器尝试清理它们时它们可能处于什么状态;它们甚至可能正在使用中。使用 Threading.Interlocked.Exchange 测试并设置一个标志以指示清理正在进行中可能是一个好主意。
顺便说一句,微软在他们的文档中没有强调几点:
恕我直言,更好的做法是采用以下原则:非自清洁资源应放置在自己的类中,其唯一目的是处理它们的清理并将对象公开以供其他地方使用;这些应该是唯一具有终结器的类。只有派生自 Object 的类才应该实现终结器;添加非自清洁资源的其他派生类应将这些资源封装到单独的自清洁类中,然后保存对它们的引用。
通常,您不想在对象上实现终结器。如果您需要对托管对象执行资源清理,您希望在其中执行此操作Dispose
并正确实施 Dispose 模式。
如果您最终实现了终结器,那么您只想访问非托管资源。
如果这是一个问题,那么处置可能比最终确定更好。