我正在调试一些C#类(比方说,Foo),它实现了Dispose-Finalize模式,即如果尚未调用Dispose,它的Finalizer正在调用Dispose()。
在Dispose()中有一个日志代码可以访问Foo的某些成员(比方说Bar)。 Bar也是某个引用类型的实例,是readonly(在Foo的构造函数中创建),并且不会暴露在Foo之外的任何地方。因此,在Foo的垃圾收集时,Bar也可能已被收集。理论上说不应该从Finalize线程访问这样的成员字段。
但是日志记录代码并不了解理论,并试图记录一些Bar的属性。并且进程在Finalizer的线程中与NullReferenceException崩溃。
我理解当您忽略理论时可能会发生不好的事情,但我没有预料到NRE:垃圾收集器是否将对收集的对象的引用设为空?或者我错过了其他什么?
答案 0 :(得分:2)
在Implementing a Dispose Method中,他们特别提醒您要从终结器中访问其他对象。这就是他们有这种模式的原因:
~MyObject()
{
Dispose(false); // False because it's being called in a finalizer
}
public void Dispose()
{
Dispose(true); // True because it was called from user code
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// here it's safe to access other CLR objects
}
// Here you dispose of any unmanaged objects
}
如果您偏离此模式 - 特别是如果终结器调用Dispose
并尝试访问可能已经处理过的其他对象,那么您将遇到问题。
另请注意,除非您正在创建分配非托管资源的类,否则不需要终结器。如果您的类只使用提供.NET对象的CLR对象或第三方库,则不需要终结器。