繁体   English   中英

C#中的继承和析构函数

[英]Inheritance and Destructors in C#

根据 ,它指出Destructors cannot be inherited or overloaded. 在我的例子中,对于所有子类,析构函数将是相同的。 这几乎告诉我,我必须在每个子类中定义相同的析构函数。 我无法在基类中声明析构函数并使处理具有破坏性? 说我有这样的事情:

class A
{
    ~A()
    {
        SomethingA();
    }

}

class B : A
{

}

B b = new B();

B被破坏时,它的析构函数不会被调用?

据此,它声明Destructors不能被继承或过载。

正确。 析构函数不是可继承的成员,并且不是虚拟的,因此不能被覆盖。 它们总是具有相同的签名,因此不会超载。

在我的例子中,对于所有子类,析构函数将是相同的。

您提出这样一个基本问题的事实告诉我, 您不应该首先实现析构函数。 正确地实现析构函数是C#中最难做的事情之一,除了最微不足道的情况。 为什么你认为你需要实现析构函数?

这几乎告诉我,我必须在每个子类中定义相同的析构函数吗?

一点都不。 你是如何从析构函数不被继承的事实得出这个结论的?

我无法在基类中声明析构函数并使处理具有破坏性?

当然,这是一件明智的事情,只要你一开始就倾向于实现析构函数。

当B被破坏时,它的析构函数不会被调用?

那是不对的。

在我看来,你自己尝试它的时间要比在这里提出问题并等待回应的时间少得多。

什么时候析构函数被调用? 当变量超出范围时,它是否在垃圾收集中?

我之前的推测是正确的。 在深入了解整个垃圾收集过程之前,您绝对不应该实现析构函数。 例如,您认为变量在超出范围时收集的事实表明您不能深入理解这一点以编写正确的析构函数。

当收集器确定某个对象无法从gc根目录中访问时,该对象具有一个尚未被抑制的终结器,则该对象通过将其放置在终结队列上以由终结器线程进行服务来提升到下一代。 如果没有,它的内存将被回收。

当终结器线程开始运行时,它会运行对象的所有析构函数。 (析构函数将按照从大多数派生到最少派生的顺序运行。)在该过程之后,对象可能会或可能不会无法访问,并且可能会或可能不会抑制终结。 如果确定该对象不可达,则整个过程再次开始。

我无法强调你需要多少了解GC过程才能正确地做到这一点。 当你编写析构函数时,它会在没有任何意义的环境中运行。 对象中的所有引用可能是仅由终结器队列生根的对象; 通常所有参考都是为了生活。 引用可能是已经完成的对象。 析构函数在不同的线程上运行。 即使构造函数失败,析构函数也会运行,因此甚至可能无法正确构造对象。 非原子值类型的字段可能只是部分写入 - 当线程被中止时,双字段完全有可能只有构造函数设置的四个字节; 终结器将看到部分写入的字段。 即使对象因中止的事务而处于不一致状态,析构也会运行。 等等。 在编写析构函数时,你必须非常防守

这个答案也可能有所帮助:

我什么时候应该创建一个析构函数?

它不是C#中的析构函数。 它被称为Finializer; 当它被调用时是非确定性的。 实际上你根本不能指望它被调用。

终结器用作清理非托管资源的最后手段。 您应该查看Dispose模式

当B的实例被销毁时,将在A中定义的终结器被调用。

如果在A和B中定义终结器,则最具体的终结器(B)将首先运行,然后运行最不具体(A)。

我已经做了近十年的.NET编程。 我实现终结器的唯一一次,它最终成为内存泄漏的原因,没有别的。 你几乎从不需要它们。

一个快速的控制台应用程序可以帮助测试这种事情。

using System;

class A
{
    ~A() => Console.WriteLine("~A");       
}

class B : A
{
    ~B() => Console.WriteLine("~B");
}

public class Program
{
    public static void Main() => new B();        
}

输出可能是......

~B
~A

如果为B定义析构函数,则会调用它,然后是A。 请参阅您提供的链接底部示例

好吧,我不知道析构函数,但是你有其他有用的方法可以清理IDisposable中的Finalize()和Dispose()。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM