这是显示出令人惊讶的终结行为的示例程序:

class Something
{
    public void DoSomething()
    {
        Console.WriteLine("Doing something");
    }
    ~Something()
    {
        Console.WriteLine("Called finalizer");
    }
}

namespace TestGC
{
    class Program
    {
        static void Main(string[] args)
        {
           var s = new Something();
           s.DoSomething();
           GC.Collect();
           //GC.WaitForPendingFinalizers();
           s.DoSomething();
           Console.ReadKey();
        }
    }
}

如果我运行程序,打印出来的是:
Doing something
Doing something
Called finalizer

这似乎符合预期。因为在调用 GC.Collect() 之后有对 s 的引用,所以 s 不是垃圾。

现在从 //GC.WaitForPendingFinalizers(); 行中删除注释
再次构建并运行程序。

我希望输出不会发生任何变化。这是因为我读到 如果发现对象是垃圾并且它有一个终结器,它将被放入终结器队列 。由于对象不是垃圾,因此不应将其放入终结器队列似乎是合乎逻辑的。因此,注释掉的行应该什么都不做。

但是,程序的输出是:
Doing something
Called finalizer
Doing something

有人可以帮助我理解为什么调用终结器吗?

最佳答案

我无法在笔记本电脑上重现该问题,但您的 DoSomething 方法不使用对象中的任何字段。这意味着即使 DoSomething 正在运行,对象也可以被最终确定。

如果您将代码更改为:

class Something
{
    int x = 10;

    public void DoSomething()
    {
        x++;
        Console.WriteLine("Doing something");
        Console.WriteLine("x = {0}", x);
    }
    ~Something()
    {
        Console.WriteLine("Called finalizer");
    }
}

...然后我怀疑你总是会看到 DoingSomething 在“Called finalizer”之前打印两次——尽管最终的“x = 12”可能在“Called finalizer”之后打印。

基本上,终结器可能有点令人惊讶——我很少发现自己使用它,并鼓励您尽可能避免使用终结器。

关于c# - 为什么在对象上调用终结器,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/20731667/

10-10 18:29