析构函数和终结符之间的区别?

时间:2009-12-09 09:40:50

标签: c# terminology destructor finalizer

请注意:这个问题是关于“析构函数”和“终结器”之间的术语差异及其正确用法。我只提供了在C#和C ++ / CLI中使用它们的示例,以说明我提出问题的原因。我很清楚它是如何在C#和CLR中实现的,但我问的是术语的正确用法。


在C#世界中,术语“析构函数”和“终结器”似乎几乎可以互换使用,我怀疑是因为C#规范使用“析构函数”一词来描述非确定性清理功能,而CLR文档总是使用“终结器”这个词,因此在C#领域内他们的意思相同。

但是,在C ++ / CLI规范中,两者之间存在差异。它允许确定性和非确定性清理,并使用术语“析构函数”作为确定性功能,使用“终结器”作为非确定性功能:

  

终结器提供非确定性清理。终结器是在垃圾收集期间执行的“最后机会”函数,通常在未执行析构函数的对象上执行。

此外,维基百科对destructorfinalizer的描述表明析构函数和终结符是单独的概念,并支持C ++ / CLI规范对决定论的使用:

  

与析构函数不同,终结符不是确定性的。当程序显式释放对象时,将运行析构函数。相反,当内部垃圾收集系统释放对象时,执行终结器。

问题:

  • 从计算机科学的角度来看,“析构函数”和“终结器”之间是否存在明确定义的区别,或者术语是否只能在上下文中定义?

  • 如果存在明确的差异,那么为什么C#规范会使用“错误”的术语?

4 个答案:

答案 0 :(得分:39)

1)在行业或学术界使用的“析构者”和“终结者”之间是否有明确的区别?

肯定有。差异似乎是析构函数是确定性调用的清理方法,而终结器在垃圾收集器告诉它们时运行。

2)在这种情况下,C#规范错了 - 终结器在C#中称为“析构函数”。为什么C#规范的作者弄错了?

我不知道,但我可以猜到。事实上,我有两个猜测。

猜测#1是在1999年5月12日,没有维基百科的文章清楚地描述了这两个概念之间的细微差别。那是因为没有维基百科。还记得没有维基百科吗?黑暗时代,男人。错误可能只是一个诚实的错误,相信这两个术语是相同的。

哎呀,据我所知,1999年5月12日两个词 相同,而且定义的差异只是后来发展的,因为很明显需要消除歧义之间的歧义和懒惰的清理方法。

猜测#2是在1999年5月12日,语言设计委员会希望保留“析构函数”可以被实现为其他而不是终结者的可能性。也就是说,“析构函数”被设计为C#语言概念,它不一定与.NET“终结器”概念一对一地映射。当设计一种语言与它所在的框架同时进行设计时,有时候你想要让自己与子系统中最新的设计更改保持隔离。

语言委员会1999年5月12日的笔记部分阅读:

  

我们将使用这个词   执行成员的“析构函数”   当一个实例被回收。类   可以有破坏者;结构不能。   与C ++不同,析构函数不可能   明确地叫。毁灭是   非确定性的 - 你不能可靠   知道析构函数何时执行,   除了说它在某些地方执行   在所有引用之后指向   对象已被释放。该   继承链中的析构函数   大多数人按顺序调用   后代至少后代。那里   没有必要(也没有办法)   派生类要显式调用   基础析构函数。 C#编译器   编译析构函数   适当的CLR表示。对于   这个版本可能意味着一个   实例终结器即   区分元数据。 CLR可能   提供静态终结器   未来;我们没有看到任何障碍   C#使用静态终结器。

所以,在那里,你现在知道我在这个问题上所知道的一切。如果您想了解更多信息,请在下次见到他时询问Anders。

答案 1 :(得分:6)

严格来说C#没有​​析构函数(我相信C#规范在这一点上令人困惑)。 C#的终结器看起来像C ++析构函数,但正如你所说,它是非确定性的。 C#以IDisposable::Dispose的形式进行确定性清理(但这仍然不称为析构函数)。

C ++ / CLI具有确定性析构函数,看起来像C ++析构函数。在CLI级别,这些映射到IDisposable::Dispose()IDisposable已为您实现)。 C ++ / CLI具有非确定性终结器,看起来像析构函数但使用!前缀而不是^前缀。

C ++ / CLI使用与C#用于终结器的析构函数相同的语法这一事实可能有点令人困惑 - 但它更适合C ++,后者具有确定性破坏的强大传统。

不幸的是,缺乏这些术语的通用定义意味着你总是需要澄清你在说什么。我个人总是在谈论C#概念时使用 finalizer 这个词,并为上下文保留析构函数,它肯定意味着确定性破坏。

[更新] 自从我在这里发帖以来,Eric Lippert已经发布了他的回复(接受的回答),并且同时跟着blog post。我很高兴看到我们达成一致: - )

答案 2 :(得分:4)

我相信'析构函数'是C#代码,'终结器'是编译后的CIL方法。 C#编译器将析构函数转换为终结器。

编辑:更加冗长; C#语言规范将“析构函数”定义为类上的C#实例方法。因此,'析构函数'是C#语法的一部分 - 析构函数是出现在源代码中的语言对象。

  

析构函数是实现破坏类实例所需操作的成员。析构函数不能包含参数,它们不能具有可访问性修饰符,也不能显式调用它们。在垃圾收集期间会自动调用实例的析构函数。

'终结器'是公共语言运行时中使用的术语,例如在调用GC.WaitForPendingFinalizers()时,它指的是任何.net语言,而不仅仅是C#。任何.net引用类型都可以有终结器。对于C#类(源代码工件),析构函数编译为CIL方法,这是CLR类型的终结器。

或者更简洁,析构函数终结者,因为源代码机器代码;)

答案 3 :(得分:3)

如果我们遵守析构函数的“确定性”定义,那么我会说.NET对象没有析构函数,除非使用the IDisposable interface明确实现。