即使在代码范围 + 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# 的新手,所以如果我说的东西不完全理解或者我写的代码有问题,请原谅。
我在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# 的新手,所以如果我说的东西不完全理解或者我写的代码有问题,请原谅。