c# 我的析构函数没有被调用?

c# my destructor isn't being called?

我有这个简单的代码并试图调用析构函数但我不能调用它:(

我知道 GarbageCollector 在必要时运行,所以我使用了 GC.WaitForPendingFinalizers();但也没用。

这是我的代码:

class Program
    {
        static void Main(string[] args)
        {
            Calculator calculator = new Calculator();
            Console.WriteLine("{0} / {1} = {2}", 120, 15, calculator.Divide(120, 15)

            GC.Collect();
            GC.WaitForPendingFinalizers();

            Console.WriteLine("Program finishing");                           
        }

  }

class Calculator
    {

        // Constructor
        public Calculator()
        {
            Console.WriteLine("Calculator being created");
        }

        // Public Divide method
        public int Divide(int first, int second)
        {
            return first / second;
        }

        // Destructor
        ~Calculator()
        {
            Console.WriteLine("Destructor is called");

        }

    }

这是我的输出:

Calculator being created

120 / 15 = 8

Program finishing

我做错了什么?为什么我看不到 "Destructor is called"?

我不建议真正使用 destructors .net

无论如何,在你的情况下,GC 在你调用 GS 时不认为你的对象是垃圾,因为你的堆栈 计算器中有 alive link,这很重要在堆中反对 所以你可以尝试修改这段代码

main(){
  DoCalculations();
  //at this point object calculator is garbage (because it was allocated in stack)
  GC.Collect();
}
DoCalculations(){
  Calculator calculator = new Calculator(); // object allocated
  calcualtor.doSomething();  //link alive
}

如果您 运行 一个附加了调试器的程序,它会改变对象生命周期的行为。

在没有调试器的情况下,一旦在代码中传递了对象的最后一次使用,对象就会变得可收集以供收集。附加调试器后,所有对象的生命周期都会延长到对象在范围内的整个时间,这样做是为了让您可以在调试器的 Watch window 中查看对象,而不是拥有对象从你下面收集出来。

您必须 运行 您的程序在没有附加调试器的情况下处于发布模式,或者在调用 GC.Collect() 之前将 calculator 设置为 null 才能拥有该对象有资格进行垃圾收集并拥有它的终结器 运行.

lifetime of a local variable在局部变量作用域内激活控制的lifetime声明它。所以你的当地人一直活着直到主要结束。仅此一点就足以解释为什么不收录了,但这里有一些微妙之处,我们应该更深入地探讨。

可以通过多种机制延长生命周期,包括通过 lambda、迭代器块、异步方法等捕获外部变量。

允许在抖动可以证明这样做对单线程控制流没有影响的情况下缩短生命周期。 (您可以使用 KeepAlive 来确保这种缩短不会在您必须避免的情况下发生。)

在你的情况下,运行时间是允许注意到本地不再被读取,将其标记为过早死亡,从而使引用成为孤儿到对象,然后将被收集并最终确定。 不需要这样做,显然,在你的情况下,不需要。

正如另一个答案正确指出的那样:如果 GC 检测到调试器正在 运行ning,它会故意抑制此优化,因为在您检查一个对象时收集对象是一种糟糕的用户体验在调试器中包含对它的引用的变量!

让我们考虑一下我关于缩短寿命的陈述的含义,因为我认为您可能没有完全理解这些含义。

  • 允许 运行时间注意到 ctor 从未访问过此

  • 运行时间允许注意divide从来不访问这个.

  • 运行时间允许注意因此本地从未真正被读取和使用

  • 因此允许对象在其生命周期的任何时候都不会在 GC 中成为根

  • 这意味着垃圾收集器被允许运行构造器之前的终结器。

GC 和终结器 运行 在它们自己的线程上,记住;操作系统可以在任何时候挂起主线程并切换到 gc 和终结器线程,包括分配器 运行s 之后但控制权传递给构造函数之前。

在你写的那种场景中,绝对疯狂的事情是允许发生的;不是 运行ning 的终结器是你问题中最少的!可怕的是可能 运行。

如果您没有立即清楚这一事实,那么您没有必要编写终结器。编写正确的终结器是 C# 中最困难的事情之一。如果您不是 CLR 垃圾收集器语义的所有细节方面的专家,那么您不应该编写终结器。

有关编写终结器有多困难的更多想法,请参阅我关于该主题的系列文章,这些文章从这里开始:

https://ericlippert.com/2015/05/18/when-everything-you-know-is-wrong-part-one/