即使在代码范围 + GC 之后也没有调用 C# 析构函数?

C# destructor not called even after code scope + GC?

我在VS2019下的Nunit+.net core 2.0有一个简单的测试代码:

public class Tests
{
    public static int i = 0;
    class First
    {
        ~First()
        {
            i += 1;
            Console.WriteLine("First's destructor is called.");
        }
    }

    class Second : First
    {
        ~Second() { i += 10; Console.WriteLine("Second's destructor is called."); }
    }

    class Third : Second
    {
        ~Third() { i += 100; Console.WriteLine("Third's destructor is called."); }
    }
    [Test]
    public static void Test()
    {
        {
            Third t = new Third();
        }
        Thread.Sleep(1000);
        System.GC.Collect();
        Assert.AreEqual(111, i);
    }
}

它总是失败,而我最终发现 i=0,并且在 Test() 之后立即调用析构函数。但是可以看到"t"是在一个代码块里,代码块后面是无效的,我也调用了

System.GC.Collect();

在我的 "Assert".

之前

为什么GC之后还不调用析构函数?如何修复我的代码以使测试通过?

非常感谢。

GC.Collect()

之后调用 GC.WaitForPendingFinalizers()

终结器(不是析构函数)运行 作为异步后台任务,因此在您调用 GC.Collect() 并到达断言语句后,不能保证终结器已经完成。

使用 using 语句和 IDisposable interface 而不是析构函数;

 public class Tests
{
    public static int i = 0;
    class First : IDisposable
    {
        public virtual void Dispose()
        {
            i += 1; Console.WriteLine("First's Dispose is called.");
        }
    }

    class Second : First
    {
        public override void Dispose()
        {
            i += 10; Console.WriteLine("Second's Dispose is called.");
            base.Dispose();
        }
    }

    class Third : Second
    {
        public override void Dispose()
        {
            i += 100; Console.WriteLine("Third's Dispose is called.");
            base.Dispose();
        }
    }
    public static void Test()
    {
        using (Third t = new Third()) {
            Console.WriteLine("Now everything will be ok, after leaving this block");
            Console.WriteLine("t object will be dispose");
        }
        Thread.Sleep(1000);
        System.GC.Collect();
        Assert.AreEqual(111, i);
    }
}

你可以调用System.GC.Collect()但没关系。析构函数不会像您期望的那样立即被调用。 (在上面的代码中你不需要 System.GC.Collect())

令我感到奇怪的是,为什么 Microsoft 没有更清楚地说明这一点,而不是旧的定义 "Forces an immediate garbage collection of all generations.",这导致开发人员认为他们应该期望立即得到结果,并且GC.Collect() 应该等待 垃圾收集在它之前完成 returns?!

GC.Collect Method(System) - Microsoft Docs

对我来说更奇怪的是,他们给出的代码作为使用 GC.Collect() 的示例出于同样的原因,就像他们声称的那样工作。您可以通过评论 GC.Collect(); 行来尝试自己。我不知道,也许他们只是不经常更新文档。也许GC.Collect()之前的情况不一样?!如果您问我,GC.CollectRequest() 将是此函数的更合适的名称。

P.s。我是 C# 的新手,所以如果我说的东西不完全理解或者我写的代码有问题,请原谅。