【问题标题】:C# destructor not called even after code scope + GC? [duplicate]即使在代码范围 + GC 之后也没有调用 C# 析构函数? [复制]
【发布时间】:2020-05-08 17:03:35
【问题描述】:

我在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();

在我的“断言”之前。

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

非常感谢。

【问题讨论】:

  • 你至少需要给GC.WaitForPendingFinalizers打个电话。如果您在调试版本中,它可能还会出于调试原因保留t:变量的范围几乎与它何时可用于 GC 完全无关
  • 一般来说,在 C# 中,很少需要编写终结器。写出好的定稿是非常困难的。如果您想要确定性完成(即,您希望清理代码在特定时间运行),请查看 IDisposableusing 和 Dispose 模式

标签: c# testing garbage-collection nunit destructor


【解决方案1】:

GC.Collect()之后调用GC.WaitForPendingFinalizers()

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

【讨论】:

  • 我在 GC.Collect() 后面加了这行,但还是不行,Assert 还是因为“i=0”而失败。
  • 你是在 Release 还是 Debug 中运行?
  • 处于调试模式。谢谢。
  • 我也尝试在发布模式下运行它。结果相同
  • 奇怪。我在 NUnit 中运行完全相同的代码并且测试通过了。也许将花括号内的内容提​​取到不同的方法中,看看是否有帮助?
【解决方案2】:

使用 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()

我很奇怪为什么微软没有更清楚地说明这一点,而不是旧的定义“强制所有代的立即垃圾收集。”这导致开发人员认为他们应该期望立即得到结果并且 GC.Collect() 应该等待垃圾回收完成后再返回?!

GC.Collect Method(System) - Microsoft Docs

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-09-26
    • 1970-01-01
    • 2012-06-14
    • 2014-09-18
    • 2018-09-15
    • 2014-11-09
    • 1970-01-01
    • 2013-12-25
    相关资源
    最近更新 更多