在C#析构函数(Finalizer)中,您是否无法访问类的托管成员?如果是真的,为什么呢? 您知道的其他C#终结者限制是什么?
示例:
class MyClass
{
private FileStream _fs;
private IntPtr _handle;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~MyClass()
{
Dispose(false);
}
private void Dispose(bool isDisposing)
{
if (isDisposing)
{
_fs.Dispose(); // Won't be accessed from destructor
}
//some way to release '_handle' - this happans anyway (Called by Dispose or by Destructor)
}
}
答案 0 :(得分:1)
你可以。但是重点是什么?您所在的对象已无法访问,因此所有这些托管资源都已被GC收集。因此,无需调用处理它们。
来自msdn
使用析构函数发布资源: 通常,当您使用不以垃圾收集为目标的运行时语言进行开发时,C#不需要所需的内存管理。这是因为.NET Framework垃圾收集器隐式管理对象的内存分配和释放。但是,当您的应用程序封装非托管资源(如窗口,文件和网络连接)时,您应该使用析构函数来释放这些资源。当对象符合销毁条件时,垃圾收集器将运行该对象的Finalize方法。
显式发布资源 如果您的应用程序使用昂贵的外部资源,我们还建议您在垃圾收集器释放对象之前提供显式释放资源的方法。您可以通过从IDisposable接口实现Dispose方法来执行此操作,该方法对对象执行必要的清理。这可以显着提高应用程序的性能。即使对资源进行了这种显式控制,如果对Dispose方法的调用失败,析构函数也会成为清理资源的安全措施。
答案 1 :(得分:1)
是的,您不应该从终结器访问其他托管类,或者在终结器调用该方法时从Dispose
访问。到执行对象的终结器时,它曾经引用的任何托管对象的状态都是不确定的。它们可能仍然存在,可能正在等待自己完成,或者可能已经被垃圾收集。此外,终结器在不同的线程上运行。
答案 2 :(得分:1)
这是一个.NET 1.x实现细节,难以置信难以摆脱。部分问题必须是大多数关于.NET的书都是在2005年之前编写的,它们都有关于终结器的章节,而且它们都有错误。
粗略地说,如果你认为你需要一个终结者,那么99.9%的时间你都错了。可终结的资源需要包装在自己的类中, 类需要有一个终结器。如果您想,您需要实施一次性模式,那么您在95%的时间内都会出错。这绝对错了。但是,如果你的基类已经犯了实现它的错误,有时候你没有选择权。一些.NET Framework类存在这个错误,这是微软无法解决的问题。
上一章中带有终结器的类是不你应该自己实现的。它已经编写,.NET 2.0获得了SafeHandle
类。如果Release()方法不是框架中已经提供的方法,那么您最多可能需要派生自己的类。 SafeBuffer对基于指针的资源非常有用。
最大的区别在于这些类非常特殊,它们有关键终结器。这是一个昂贵的词,这意味着它们可以保证运行,即使终结器线程在未处理的异常上遭到轰炸。在大多数LOB应用程序中实际上并不重要,如果它们熄火,那么操作系统清理通常就足够了。但是,当.NET代码在具有长时间运行时间保证的主机中运行时,这一点非常重要SQL Server就是一个很好的例子,不得不重新启动它,因为过多的SQL / CLR代码被轰炸和泄漏的句柄对业务不利。