【问题标题】:The difference between a destructor and a finalizer?析构函数和终结函数的区别?
【发布时间】:2010-12-24 18:26:18
【问题描述】:

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


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

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

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

此外,destructorfinalizer 的 Wikipedia 描述表明析构函数和终结函数是独立的概念,并支持 C++/CLI 规范对确定性术语的使用:

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

问题:

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

    李>
  • 如果有明确定义的区别,那么为什么 C# 规范会使用“错误”的术语?

【问题讨论】:

    标签: c# terminology destructor finalizer


    【解决方案1】:

    1) 工业界或学术界使用的“析构函数”和“终结器”之间是否有明确的区别?

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

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

    我不知道,但我能猜到。其实我有两种猜测。

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

    见鬼,据我所知,这两个术语在 1999 年 5 月 12 日是相同的,而且定义上的差异只是在后来才发展起来的,因为很明显有必要消除 Eager 之间的歧义和惰性清理方法。

    猜测 #2 是在 1999 年 5 月 12 日,语言设计委员会希望保留“析构函数”可以实现为 其他终结器的可能性。也就是说,“析构函数”被设计为 C# 语言概念,不一定与 .NET“终结器”概念一一对应。在设计一种语言的同时,它所在的框架也在设计中,有时您希望自己免受子系统中最新的设计更改的影响。

    语言委员会 1999 年 5 月 12 日的说明部分内容如下:

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

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

    【讨论】:

    • 有趣;从注释来看,当时的析构函数并不意味着确定性,所以我认为你可能是对的,这是一种从那时起演变而来的区别。
    【解决方案2】:

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

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

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

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

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

    【讨论】:

    • “问题”是 C# 确实将其称为析构函数。
    • 是的,我记得在我第一次回答之后 - 它使用了这两个术语 - 因此我添加了关于它令人困惑的评论。称它们为终结器更清楚也更正确。
    • +1 我认为这是最好的答案。看起来 C# 规范的术语有点误导。也许它是由实习生写的:P
    • 在 C# 3.5 的定义中,没有使用“终结器”这个词——只有“析构函数”这个词,它被定义为“实现析构实例所需操作的成员”一个类”C# 确实有析构函数,而不是终结器。具有终结器的是 CLR。​​
    • @Steve 您是正确的,C# 规范将语言功能称为析构函数,它实现了终结器概念。我仍然说这令人困惑。
    【解决方案3】:

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

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

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

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

    或者更简洁地说,destructorfinalizer 就像 source codemachine code ;)

    【讨论】:

    • 我知道它们映射到同一个东西。我问的是术语的正确使用。
    • 嗨,格雷格。我不是在谈论它们映射到同一个东西。我的意思是,一个术语指的是源代码人工制品,另一个指的是编译的机器代码人工制品。析构函数在 C# 语言规范中被提及并且是 C# 语法的一部分;终结者不是。但是,垃圾收集(与语言无关)指的是终结器;例如,在像 GC.WaitForPendingFinalizers() 这样的调用中;因此,Finalizer 是一个 CLR 术语,指的是在释放 .net 对象的内存之前进行清理的过程。我也会尝试编辑我的帖子以澄清它
    • C# 没有析构函数,只有终结器。它与 C++ 的析构函数具有相似的语法这一事实并不重要。
    • 我们可能在这里非常同意。 C# 的析构函数不像 C++ 的析构函数那样做。他们不是一回事。但是规范在 C# 语言的上下文中定义“析构函数”这个词,所以它肯定有它们。它们只是与 C++ 概念无关。
    • @sixlettervariables:~ClassName 后面用大括号括起来的 C# 程序部分是析构函数。如果一个类有析构函数,C# 编译器将自动生成一个包含析构函数代码加上一些样板的终结器。样板代码似乎是由不知道IDisposable 模式会是什么样子的人设计的(它很可能早于IDisposable 模式),但是由于代码出现在终结器中,而没有出现在析构函数,这些术语并不完全是同义词。
    【解决方案4】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-10
      • 2020-10-18
      • 1970-01-01
      • 2021-06-01
      • 2023-03-18
      • 2011-01-16
      • 1970-01-01
      相关资源
      最近更新 更多