C# 为什么Finalize/Destructor示例在.NETCore中不起作用?
我试图学习C#中的终结和析构函数是如何工作的,我试图运行示例中的代码(粘贴代码副本,不做任何更改),但输出与预期不一样,这表明析构函数从未被调用 代码是:C# 为什么Finalize/Destructor示例在.NETCore中不起作用?,c#,.net,.net-core,destructor,C#,.net,.net Core,Destructor,我试图学习C#中的终结和析构函数是如何工作的,我试图运行示例中的代码(粘贴代码副本,不做任何更改),但输出与预期不一样,这表明析构函数从未被调用 代码是: using System; using System.Diagnostics; public class ExampleClass { Stopwatch sw; public ExampleClass() { sw = Stopwatch.StartNew(); Console.WriteLin
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
该示例显示如下输出:
实例化对象
ExampleClass的此实例已存在00:00:00.0011060
终结对象
ExampleClass的此实例已存在00:00:00.0036294
当我使用dotnet core应用程序时,代码不起作用:
实际输出为:
PS C:\ws\test> dotnet run
Instantiated object
This instance of ExampleClass has been in existence for 00:00:00.0056874
PS C:\ws\test>dotnet运行
实例化对象
ExampleClass的此实例已存在00:00:00.0056874
那么这在.NET Core中有什么不同呢?在垃圾回收器运行之前不会发生终结。除非需要(例如,内存不足)或需要,否则不会运行垃圾收集 尝试添加
System.GC.Collect();
查看终结器是否在这种情况下运行。将Peter Duniho和Henk Holterman评论中的信息汇集在一起,并进一步扩展: 这种行为违反了Microsoft和的C#5.0规范,其中规定: 在应用程序终止之前,将调用其所有尚未被垃圾收集的对象的析构函数,除非此类清理已被抑制(例如,通过调用库方法
GC.SuppressFinalize
)
但这不是一个bug,.Net Core故意偏离.Net Framework行为,如中所述:
目前,已尽最大努力尝试在关机期间为所有可终结对象(包括可访问对象)运行终结器。为可访问对象运行终结器是不可靠的,因为对象处于未定义状态
提议
关闭时不运行终结器(对于可访问或不可访问的对象)
根据该提案,不能保证所有可终结对象在关闭前都能终结
大概是由于这个原因,削弱了这个要求,所以.Net Core没有违反这个版本的规范:
在应用程序终止之前,实现应尽一切合理努力为其所有尚未被垃圾收集的对象调用终结器(§15.13),除非此类清理已被抑制(例如,通过调用库方法GC.SuppressFinalize
)。实现应该记录任何无法保证这种行为的条件
添加
GC.Collect
没有任何区别。但除此之外,我的困惑是MSDN示例不使用GC.Collect,但它显示终结器按预期调用。GC应该在程序退出之前运行。根据C#规范:“在应用程序终止之前,调用其所有尚未被垃圾收集的对象的析构函数”。问题似乎很清楚,是“为什么.NET Core不符合规范?”,而不是“我如何解决这个问题?”添加GC.Collect()
确实会有所不同,但只是在发布模式下。这是因为在调试模式下,局部变量仍在作用域中时不会被收集。虽然这并不能真正回答问题。“这是一个语言特性,对吗?”——没有语言特性可以保证调用终结器。这就是为什么任何代码都不应该假设会调用一个。终结器严格地说是作为备份,以减轻错误代码造成的伤害。作为记录,使用您的代码示例,我重现了您描述的问题,不,我不知道.NET Core在正常程序退出时无法终结对象的原因。听起来像个虫子。规范似乎相当清晰,因此除非.NET Core中有某种东西,通过设计,在正常程序终止之前,“抑制”对象的终结,.NET Core违反了规范。我认为这是一个@fluer-您提到的规范是关于C语言的。在我看来,这似乎超越了它的界限,这是关于平台的行为,而不是语言。但我认为这还不够,所以我创建了。他们禁用了终结器线程超时。加克。好吧,从那以后,除了“不要做所有的事”之外,别无选择。弱酱汁。“ECMA当前的C#5.0规范草案”——切题,但是:什么?我查看了链接,看起来确实是C#5的规范(规范中没有C#6特性)。ECMA似乎落后于C版本。“.Net核心不违反此版本的规范”--我不确定我是否同意。这些变化表明了行为可能有所不同的原因,但没有证据表明.NETCore“尽了一切合理的努力”,也没有证据表明该行为已被记录在案。也许他们正朝着这个方向前进,但似乎还没有到达那里(@PeterDuniho是的,ECMA有很多工作要做。但是C#任务组由Jon Skeet领导,所以我相信不会花那么长时间。我同意ECMA草案的措辞与.Net核心行为不完全一致,这就是我创建的原因。你可以补充一点,这现在已经有了完整的文档:“in.Net Framework应用程序(但不是在.NET核心应用程序中),程序退出时也会调用终结器。”