析构函数限制 - 从析构函数访问托管成员

时间:2015-03-12 14:30:30

标签: c# memory destructor finalizer

在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)
    }
}

3 个答案:

答案 0 :(得分:1)

你可以。但是重点是什么?您所在的对象已无法访问,因此所有这些托管资源都已被GC收集。因此,无需调用处理它们。

来自msdn

  

使用析构函数发布资源:    通常,当您使用不以垃圾收集为目标的运行时语言进行开发时,C#不需要所需的内存管理。这是因为.NET Framework垃圾收集器隐式管理对象的内存分配和释放。但是,当您的应用程序封装非托管资源(如窗口,文件和网络连接)时,您应该使用析构函数来释放这些资源。当对象符合销毁条件时,垃圾收集器将运行该对象的Finalize方法。

     

显式发布资源   如果您的应用程序使用昂贵的外部资源,我们还建议您在垃圾收集器释放对象之前提供显式释放资源的方法。您可以通过从IDisposable接口实现Dispose方法来执行此操作,该方法对对象执行必要的清理。这可以显着提高应用程序的性能。即使对资源进行了这种显式控制,如果对Dispose方法的调用失败,析构函数也会成为清理资源的安全措施。

https://msdn.microsoft.com/en-us/library/66x5fx1b.aspx

答案 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代码被轰炸和泄漏的句柄对业务不利。