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

时间:2017-06-15 17:22:22

标签: c# destructor finalize

我有这个简单的代码并尝试调用析构函数但我无法调用它:(

我知道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");

        }

    }

这是我的输出:

  

正在创建的计算器

     

120/15 = 8

     

计划完成

我做错了什么?为什么我看不到" Destructor被称为" ?

3 个答案:

答案 0 :(得分:12)

本地变量生命周期是声明它的局部变量范围内控件激活的生命周期。所以你的当地人还活着,直到主要结束。仅这一点足以解释为什么没有收集它,但这里有一些细微之处,我们应该更深入地探索。

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

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

在您的情况下,运行时允许注意到本地永远不会被再次读取,将其标记为早期死亡,从而孤立对该对象的引用,然后将其收集并且最终确定。这样做不是必需,显然,在你的情况下,没有。

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

让我们考虑一下我关于缩短生命期的陈述的含义,因为我认为你可能还没有完全掌握这些含义。

  • 允许运行时注意到 ctor永远不会访问此

  • 允许运行时注意 divide永远不会访问此

  • 允许运行时注意到本地从未实际读取和使用

  • 因此,允许该对象在其生命周期的任何时刻都不会在GC中生根

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

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

在你写的那些场景中,允许发生绝对疯狂的事情;没有运行的终结器是你问题最少的!它是可以运行的时候是可怕的。

如果您没有立即明白这一事实,那么您就没有商业写作终结者了。编写正确的终结器是C#中最难做的事情之一。如果您不是CLR垃圾收集器语义的所有细节的专家,那么您不应该编写终结器。

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

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

答案 1 :(得分:1)

如果运行附带调试器的程序,则会更改对象生命周期的行为。

如果没有调试器,只要在代码中传递了对象的最后一次使用,对象就会变得易于收集。附加调试器后,所有对象的生命周期都会扩展到对象在范围内的整个时间,这样就可以在调试器的Watch窗口中查看对象,而不是从下面收集对象你。

您必须在没有附加调试器的情况下以发布模式运行程序,或者在调用calculator之前将null设置为GC.Collect(),以使对象有资格进行垃圾回收和让它的终结者运行。

答案 2 :(得分:-1)

我不建议真正使用析构函数.net

无论如何,在您的情况下,GC在您调用GS时不认为您的对象是垃圾,因为您的堆栈中有活动链接计算器,它指向堆中的对象 所以你可以尝试修改这段代码

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
}