【问题标题】:Destructor execution order?析构函数执行顺序?
【发布时间】:2015-08-02 19:58:58
【问题描述】:

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

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

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。

显然每个班级都有一个“隐藏”的顺序,我可以相信它吗?

【问题讨论】:

  • C# 没有析构函数。您正在描述终结器。它们会减慢垃圾收集速度,从而减慢您的应用程序。因此,它们只应在边缘情况下使用,例如确保尽快释放非托管资源,并且仅在无法使用确定性IDisposable 解决方案时。
  • @DavidArno 是的,they do。当然这只是命名语义,但仍然......
  • @YuvalItzchakov 他们是终结者。这是一个误称。也许设计团队在他们将其命名为析构函数时在酒吧:p
  • @SriramSakthivel 由于 C 和 C++ 执行析构函数的方式,人们避免称它们为析构函数,但无论如何;)
  • @YuvalItzchakov 你可能对eric's article感兴趣

标签: c# destructor


【解决方案1】:

显然每个班级都有一个“隐藏”的订单,我可以相信它吗?

不,你不能。如果你看the docs,他们会尖叫:

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

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

看到由于某种原因您选择使用终结器作为清理静态资源的一种方式,您应该首先考虑这是否是正确的方法,taking into account everything destructors imply,然后至少实现@ 987654325@ 并给调用者一个处理资源的机会,同时调用GC.SupressFinalize。

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

Eric Lippert 最近 (18-05-2015) 开始了一个名为 When everything you know is wrong 的系列,谈论终结器神话,我建议你看看。

编辑:

有趣的是,Erics second post(今天发布)系列回答了这个问题:

误区:终结器以可预测的顺序运行

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

【讨论】:

    【解决方案2】:

    我建议阅读Eric Lippert's latest blog post,但由于未定义顺序,您不能依赖在未来版本中保持相同的顺序。

    【讨论】:

      【解决方案3】:

      正如之前多次尖叫的那样,终结者不保证任何顺序。你不能做任何假设。您应该假设最坏的情况(即)它可以按任何顺序执行。

      显然每个班级都有一个“隐藏”订单,我可以相信它吗?

      是和不是。

      对于普通对象,终结器的执行顺序是不可预测的。所以不行。你不能依赖其中的任何东西。

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

      此排序存在的原因在this blog post 中进行了描述。这没有记录,但由 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)
          {
          }
      }
      

      【讨论】:

      • 嗯,谢谢,这提供了有用的附加信息+1
      猜你喜欢
      • 2017-06-13
      • 2016-04-20
      • 2011-10-28
      • 2010-12-25
      • 2019-06-04
      • 2012-05-26
      • 2017-10-20
      • 2012-04-10
      相关资源
      最近更新 更多