析构函数执行顺序



我知道c#中的析构函数没有执行顺序。

以下结构我确实在几个类中使用,它用于Desturct实例和静态信息:

public class MyClass
{
    private static readonly Destructor DestructorObject = new Destructor();
    ~MyClass()
    {
        Console.WriteLine("Destructor Called");
    }
    static void Main(string[] args)
    {
        var myClass = new MyClass();
    }
    private sealed class Destructor
    {
        ~Destructor()
        {
            Console.WriteLine("Static Destructor Called");
        }
    }
}

正如我上面提到的,析构器的顺序是不确定的。但是,当我在许多类中使用这种构造时,我发现在每个类中都有一个不变的顺序,即使我重新编译应用程序并再次运行它,这个顺序也会保持不变。

意味着一个MyClass1总是可以首先运行~MyClass1,而另一个类MyClass2总是可以先运行~Destructor

由于每个类都有一个"隐藏"的顺序,我可以信任它吗

正如明显存在的那样;隐藏的";每节课的订单,我可以相信吗?

不,你不能。如果你看一下文件,他们会尖叫:

两个对象的终结器不能保证在任何特定顺序,即使一个对象引用另一个对象。也就是说,如果对象A具有对对象B的引用,并且两者都具有终结器,对象当对象A的终结器时,B可能已经被终结启动。

将这样的实现细节作为正常执行流的一部分是一个非常糟糕的主意。

鉴于出于某种原因,您选择使用终结器来清理静态资源,您应该首先考虑这是否是正确的方法,考虑到析构函数所暗示的一切,然后至少实现IDisposable,并给调用者一个处理资源的机会,同时调用GC.SupressFinalize

在对象中使用这种常见方法也会延长对象的寿命,因为只有当它们移动到f-reachable队列时,它们才有资格被收集,并且依赖终结器线程来实际清理它们,这根本不能保证。

Eric Lippert最近(2015年5月18日(开始了一个名为的系列节目。当你所知道的一切都是错误的谈论终结器神话时,我建议你看看。

编辑:

有趣的是,Erics系列的第二篇帖子(今天发布(回答了这个问题:

神话:终结器以可预测的顺序运行

假设我们有一个对象树,所有对象都是可终结的,并且都在终结器队列上。树从根到叶、从叶到根或任何其他顺序都不需要最终确定。

正如前面多次指出的那样,终结器不保证任何顺序。你不能做任何假设。你应该假设最坏的情况(即(它可以按任何顺序执行。

由于每个类都有一个"隐藏"的顺序,我能相信吗?

是和否

对于普通对象,终结器的执行顺序是不可预测的。所以没有。你不能依赖它。

对于从SafeHandle继承的对象,是的,有一些排序。例如:如果有两个对象准备好进行终结,一个是从SafeHandle派生的,另一个不是,那么可以保证在执行SafeHandle的终结器之前,将执行不是从SafeHandle继承的对象的终结器。

这种排序存在的原因在本博客文章中有描述。这并没有记录在案,而是由BCL团队在博客上发表的。未来不太可能改变。但是

为了证明这一点,下面的程序总是先完成Destructor类,然后再完成MyClass。因为您知道MyClass继承自SafeHandle

public class MyClass : SafeHandle
{
    private static readonly Destructor DestructorObject = new Destructor();
    ~MyClass()
    {
        Console.WriteLine("Destructor Called");
    }
    protected override bool ReleaseHandle()
    {
        return true;
    }
    public override bool IsInvalid
    {
        get { return false; }
    }
    static void Main(string[] args)
    {
        var myClass = new MyClass(IntPtr.Zero, true);
    }
    private sealed class Destructor
    {
        ~Destructor()
        {
            Console.WriteLine("Static Destructor Called");
        }
    }
    public MyClass(IntPtr invalidHandleValue, bool ownsHandle)
        : base(invalidHandleValue, ownsHandle)
    {
    }
}

我建议阅读Eric Lippert的最新博客文章,但由于订单没有定义,您不能依赖订单在未来版本中保持不变。

最新更新