.net 在终结器中访问引用类型成员变量是否安全?
换句话说,.net 在终结器中访问引用类型成员变量是否安全?,.net,finalizer,.net,Finalizer,换句话说, class Foo { object obj; Foo() { obj = new object(); } ~Foo() { obj.ToString(); /* NullReferenceException? */ } } 如果这是一个问题,那么处置可能比最终确定要好。来源: 即使一个对象引用另一个对象,也不能保证两个对象的终结器以任何特定顺序运行。也就是说,如果对象A引用了对象B,并且两者都有终结器,那么当对象A的终结器启动时,对象B可能已经终结 简而言
class Foo
{
object obj;
Foo() { obj = new object(); }
~Foo() { obj.ToString(); /* NullReferenceException? */ }
}
如果这是一个问题,那么处置可能比最终确定要好。来源: 即使一个对象引用另一个对象,也不能保证两个对象的终结器以任何特定顺序运行。也就是说,如果对象A引用了对象B,并且两者都有终结器,那么当对象A的终结器启动时,对象B可能已经终结 简而言之,在终结器期间,不能对引用对象的状态进行任何假设 在几乎所有情况下,终结器中实现的逻辑都属于一次性模式。这是一个如何使用接口在.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!=空值对您没有帮助 详情请参见此处: “概括这一原则,在Dispose方法中,清理对象持有的所有资源是安全的,无论它们是托管对象还是本机资源但是,在终结器中,清理不可终结的对象是安全的,通常终结器应该只释放本机资源。(您的obj是可终结的,因此您不应该在其他终结器中接触它) 这也是为什么你有 如果(处理){…}
在IDisposable模式中(请参见上面链接中的图2)。通常,您不希望在对象上实现终结器。如果需要执行托管对象的资源清理,您希望在
Dispose
中执行,并正确实现Dispose模式
如果最终实现了终结器,则只希望访问非托管资源。当对象注册以进行终结时,它将被放置在终结队列中。当垃圾收集器运行时,所有对象都分为三类:
更好的做法是,IMHO采用以下原则:非自清理资源应该放在它们自己的类中,这些类的唯一目的是处理它们的清理并公开对象以供其他地方使用;这些类应该是唯一具有终结器的类。只有从对象派生的类才应该实现终结器;其他派生类添加非自清理资源的类应该将这些资源封装到单独的自清理类中,然后保存对它们的引用。虽然这不是对您问题的回答,但我编写终结器和dispose方法,就像编写C代码一样,在执行之前先检查。
if(obj!=null)obj.dispose()
这只是因为在这些方法中跟踪和处理异常非常痛苦。你真的需要终结器吗?大多数C#类不应该有终结器。仅仅因为你实现了IDisposable
并不意味着你应该实现终结器。事实上,你几乎从来都不想实现终结器。然而,如果你真的实现了终结器删除终结器您还应该实现IDisposable
。只有当您的对象直接包含非托管资源时,您才需要终结器。您还应该将非托管资源句柄包装到类似SafeFileHandle
的对象中,以便生成的终结器不会对GC造成过度负载。我同意您不想这样做除非您正在处理非托管资源,否则请实现终结器。我本来希望重载Dispose
可以清楚地说明这一点,但我将重新编写代码示例,以便仅处理托管资源场景。仅针对你们;)事实上,如果您刚刚删除终结器,您的原始代码示例是正确的。为了正确地实现Dis在pose模式中,您确实需要一个公共虚拟void Dispose(bool disposing)
方法,该方法包含您的所有处置逻辑(正如您在原始代码中所做的那样),并且IDisposable.Dispose()
方法实现只需调用它即可