C# - "destructors are not inherited"实际上是什么意思?

标签 c# .net idisposable destructor finalization

C# Language Specification 3.0 的第 10.13 节,析构函数声明如下:

Destructors are not inherited. Thus, a class has no destructors other than the one which may be declared in that class.



C# Programming Guide 的析构函数部分包含一个示例,演示如何调用继承层次结构中的析构函数,包括以下语句:

...the destructors for the ... classes are called automatically, and in order, from the most-derived to the least-derived.



我已经用各种实际示例对此进行了研究,其中包括一个定义了析构函数的基类,一个继承自基类但未定义析构函数的派生类。创建派生类的实例,允许对该实例的所有引用超出范围,然后强制进行垃圾回收,这表明在派生类的实例完成时会调用基类中定义的析构函数。

我的问题是“析构函数不被继承”实际上是什么意思,因为虽然你不能显式调用析构函数,但继承链中的析构函数会被自动调用,即使派生类没有定义析构函数也会调用基类析构函数?

它是否与一些微妙的语义区别有关,即终结是由垃圾收集器而不是 C# 语言/编译器实现的?

编辑1:

虽然 C# 语言规范还声明“实例构造函数不被继承”,但与构造函数相关的行为与析构函数有显着不同,并且更适合 IMO 与“非继承”术语,如下面的示例所示:
  public class ConstructorTestBase
  {
    public ConstructorTestBase(string exampleParam)
    {
    }
  }

  public class ConstructorTest: ConstructorTestBase
  {
    public ConstructorTest(int testParam)
      : base(string.Empty)
    {
    }
  }

  ...

  // The following is valid since there is a derived class constructor defined that
  // accepts an integer parmameter.
  ConstructorTest test1 = new ConstructorTest(5);

  // The following is not valid since the base class constructor is not inherited
  // by the derived class and the derived class does not define a constructor that
  // takes a string parameter.
  ConstructorTest test2 = new ConstructorTest("Test"); 

与析构函数相关的行为与此非常不同,如下例所示,它通过仅向基类添加析构函数来扩展前面的构造函数示例。
  public class ConstructorTestBase
  {
    public ConstructorTestBase(string exampleParam)
    {
    }

    ~ConstructorTestBase()
    {
      Console.WriteLine("~ConstructorTestBase()");
    }
  }

  ...

  ConstructorTest test1 = new ConstructorTest(5);
  test1 = null;
  GC.Collect();

上面的示例演示了在派生类的实例完成时将调用基类构造函数,即使派生类没有显式定义析构函数。

我的观点很简单,我遇到了很多人,他们没有意识到或理解会发生这种情况,其中很大一部分原因是“析构函数不是继承的”语句。

编辑2:

C# 语言规范还声明了以下内容并给出了底层实现的代码示例:

Destructors are implemented by overriding the virtual method Finalize on System.Object. C# programs are not permitted to override this method or call it (or overrides of it) directly.



由于底层实现实际上是基于继承的,如上所述,我认为我的问题是有效的,而且我认为到目前为止我收到的任何回复都没有正确解决这个问题 - 什么“析构函数不被继承”实际上是什么意思?

最佳答案

这不是胜负的问题。我正在尽力理解您的问题,但您似乎全神贯注于保护自己免受完全想象中的攻击。

把它放在一边,一次一个地考虑你的问题:

My question is what does "destructors are not inherited" actually mean, since although you can't call a destructor explicitly, destructors in an inheritance chain are called automatically, and base class destructors are called even if the derived class does not define a destructor?



它实际上意味着 基类的析构函数不是派生类的成员 .

我的反问是“为什么你相信 (1) 不能直接调用析构函数,(2) 继承链中的析构函数在集合时自动调用,以及 (3) 基类析构函数被调用的事实,即使派生类没有定义构造函数,与基类的析构函数是否是派生类的成员的问题有任何关系?

Does it relate to some subtle semantic distinction that finalization is implemented by the garbage collector rather than the C# language/compiler?



不。

C# 语言是一种规范;它什么都不执行。 C# 编译器实现了规范;它当然不会“实现最终确定”。 CLR 是实现终结的工具。

无论如何,基类中的析构函数不是派生类的成员这一事实与 CLR 垃圾收集器如何实现终结无关。

最后,我们将回到 CLR 终结语义的问题,以及为什么它们与继承问题无关。

While the C# language spec also states that "instance constructors are not inherited", the behaviour in relation to constructors is significantly different from desctructors, and fits better IMO with the "not inherited" terminology



好吧,我接受你相信这一点。为什么你认为这些行为与继承问题有关?我一点也不明白你为什么相信。与继承相关的是所讨论的实体是否仅仅因为它是基类型的成员而成为派生类型的成员。

我同意不能通过派生类型的构造直接调用基类型的构造函数这一事实与构造函数不被继承的事实是一致的。我只是不明白这与是否继承析构函数的问题有密切关系。

一个更密切的事实是,当派生类缺少公共(public)无参数构造函数时,使用其默认构造函数构造具有公共(public)无参数构造函数的基类确实会调用该构造函数。派生类不继承基类的公共(public)无参数构造函数,但它仍然被调用。如果您接受可以以这种方式调用未继承的构造函数,那么为什么不接受也以类似的语义调用析构函数?

My point is simply that I have encountered many people who do not realise or understand that this what happens, and a significant part of the reason for this is the "destructors are not inherited" statement.



我完全同意。如果对正确的语义没有明确的理解,则可能不应该编写析构函数。我对 C# 初学者的书籍数量感到沮丧,它们将析构函数视为适合新手的主题;编写正确的析构函数是 C# 中最困难的基本任务之一。

Since the under-the-hood implementation is, in fact, based on inheritance, as stated above, I think my question is valid



无论如何,你的问题是完全有效的。但是现在您正在将功能的实现细节与功能的规范混为一谈。

以此类推,考虑匿名类型。有道理,他们没有名字。但是我们吐出的元数据当然会命名类型; CLR 要求类型具有名称。这是矛盾吗?并不真地。规范中称为“匿名类型”的概念实体没有名称,这很重要。实现者当然可以自由使用不需要命名所有类型的平台。

类似地,我们的 C# 实现通过将 IL 写入一个覆盖名为 Finalize 的虚拟方法的方法来实现析构函数。 C# 语言经过精心设计,不依赖于运行时的此功能。 C# 的另一个实现可以完全自由地选择其他一些机制来实现析构函数。 (对我们来说,修改规范可能是一个好主意,以更清楚地说明析构函数的实现方式是一个提供信息的实现细节,而不是 C# 语言的要求。)

但是无论选择何种实现细节,基类中的析构函数都不是派生类的成员。就像,无论实现细节如何,匿名类型都没有名称。

现在清楚了吗,或者您还有更多问题?

关于C# - "destructors are not inherited"实际上是什么意思?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1870469/

相关文章:

.net - 为什么即使我不调用服务的 Dispose() 方法也会调用它? (使用 BasicHttpBinding)

c# - 如何处理封装了一次性实例的类?

c# - .NET Core - HttpClient 与 RestSharp

.net - 如何安装支持 .NET Core 6.0 的 .NET SDK?

c# - Web 服务 Excel 转换

c# - 如何验证电话号码字段以仅接受数字和最多 12 位数字?

c# - .net有排序和搜索功能吗

c# - DRY ID一次性图案

c# - Xamarin.Forms iOS依赖服务中的触觉反馈崩溃

c# - 银光 4 : Converting image into byte[]