我正在尝试了解C#中的终结和析构函数是如何工作的,我尝试在System.Object.Finalize
示例中运行代码(代码复制粘贴,不做任何更改),但输出不同于预期,它表明析构函数永远不会被调用。
代码是:
using System;
using System.Diagnostics;
public class ExampleClass
{
Stopwatch sw;
public ExampleClass()
{
sw = Stopwatch.StartNew();
Console.WriteLine("Instantiated object");
}
public void ShowDuration()
{
Console.WriteLine("This instance of {0} has been in existence for {1}",
this, sw.Elapsed);
}
~ExampleClass()
{
Console.WriteLine("Finalizing object");
sw.Stop();
Console.WriteLine("This instance of {0} has been in existence for {1}",
this, sw.Elapsed);
}
}
public class Demo
{
public static void Main()
{
ExampleClass ex = new ExampleClass();
ex.ShowDuration();
}
}
更新
当我使用visual studio和.net framework 4.5时,代码按预期工作: 输出与示例相同:
The example displays output like the following: Instantiated object This instance of ExampleClass has been in existence for 00:00:00.0011060 Finalizing object This instance of ExampleClass has been in existence for 00:00:00.0036294
当我使用dotnet核心应用程序时,代码不起作用: 实际输出是:
PS C:\ws\test> dotnet run Instantiated object This instance of ExampleClass has been in existence for 00:00:00.0056874
那么为什么.NET Core会有所不同呢?
答案 0 :(得分:12)
将Peter Duniho和Henk Holterman的评论中的信息汇总起来并进一步扩展:
此行为违反了Microsoft和current draft of the C# 6.0 spec from Microsoft的C#5.0规范,其中说:
在应用程序终止之前,将调用尚未进行垃圾回收的所有对象的析构函数,除非已禁止此类清理(例如,通过调用库方法
GC.SuppressFinalize
)。 / p>
但它不是一个bug,.Net Core故意偏离.Net Framework行为,如a corefx issue中所述:
目前,尽最大努力在关机期间为所有可终结对象运行终结器,包括可到达的对象。为可达对象运行终结器是不可靠的,因为对象处于未定义状态。
...
提案
不要在关机时运行终结器(对于可到达或无法到达的对象)
...
根据此提案,无法保证在关闭之前完成所有可终结的对象。
大概是由于这个原因,the C# 5.0 spec from ECMA削弱了这个要求,所以.Net Core没有违反这个版本的规范:
在应用程序终止之前,实现应该尽一切合理的努力为其尚未被垃圾收集的所有对象调用终结器(第15.13节),除非已经抑制了这种清理(通过调用库方法)例如
GC.SuppressFinalize
。实现应记录无法保证此行为的任何条件。
答案 1 :(得分:-3)
最终确定不会发生,直到垃圾收集器运行。垃圾收集不会运行,除非它需要(例如你内存不足),或者force it to run。
尝试添加
System.GC.Collect();
到你的代码,看看终结器是否在那种情况下运行。