根据this,它指出Destructors cannot be inherited or overloaded.
在我的情况下,对于所有子类,析构函数将是相同的。这几乎告诉我,我必须在每个子类中定义相同的析构函数。我无法在基类中声明析构函数并使处理具有破坏性?说我有这样的事情:
class A
{
~A()
{
SomethingA();
}
}
class B : A
{
}
B b = new B();
当B
被销毁时,它的析构函数不会被调用?
答案 0 :(得分:42)
根据这一点,它声明Destructors不能被继承或重载。
正确。析构函数不是可继承的成员,并且不是虚拟的,因此不能被覆盖。它们总是具有相同的签名,因此不会超载。
就我而言,对于所有子类,析构函数都是相同的。
您提出这样一个基本问题的事实告诉我您不应该首先实现析构函数。正确实现析构函数是C#中最难做的事情之一在除了最微不足道的案件之外的所有案件中。为什么你认为你需要实现析构函数?
这是否告诉我,我必须在每个子类中定义相同的析构函数?
不,一点也不。你是如何从析构函数不被继承的事实得出这个结论的?
我无法在基类中声明析构函数并将句柄解压缩?
当然,这是一件明智的事情,只要你一开始就倾向于实现析构函数。
当B被销毁时,它的析构函数不会被调用?
这是不正确的。
在我看来,你自己尝试的时间比在这里提出问题并等待回应的时间少得多。
析构函数何时实际被调用?当变量超出范围时,它是否在垃圾收集中?
我之前的推测是正确的。 在您深入了解整个垃圾收集过程之前,您绝对不应该实现析构函数。例如,您认为变量在超出范围时收集的事实表明您没有深刻理解这一点,写出正确的析构函数。
当收集器确定某个对象无法从gc根目录中访问,并且该对象具有一个尚未被抑制的终结器时,该对象将被放置在终结队列上,以便通过该对象进行维护,从而将该对象提升为下一代。终结者线程。如果没有,则回收其内存。
当终结器线程开始运行时,它会运行该对象的所有析构函数。 (析构函数将按照从大多数派生到最少派生的顺序运行。)在该过程之后,对象可能会或可能不会无法访问,并且可能会或可能不会抑制终结。如果确定该对象无法访问,则整个过程再次开始。
我无法强调您为了正确执行此操作而需要了解GC流程的程度。当你编写析构函数时,它会在没有意义的环境中运行。对象中的所有引用可能是仅由终结器队列生根的对象;通常所有参考都是为了生活。引用可能是已经完成的对象。析构函数在不同的线程上运行。即使构造函数失败,析构函数也会运行,因此对象可能甚至不能构造。 非原子值类型的字段可能只是部分写入 - 当一个线程被中止时,双字段完全有可能只有四个字节由构造函数设置;终结器将看到部分写入的字段。即使对象因中止的事务而处于不一致状态,析构也会运行。等等。在编写析构函数时,你必须极其防御。
这个答案也可能会有所帮助:
答案 1 :(得分:18)
它不是C#中的析构函数。它被称为Finializer;当它被调用时是非确定性的。实际上你根本不能指望它被调用。
终结器用作清理非托管资源的最后手段。您应该查看Dispose pattern。
答案 2 :(得分:7)
当B的实例被销毁时,将在A中定义终结器。
如果在A和B中定义终结器,则最具体的终结器(B)将首先运行,然后运行最不具体的(A)。
答案 3 :(得分:2)
我已经做了近十年的.NET编程。 仅时间我实现了终结器,它最终成为内存泄漏的原因而没有别的。你几乎从不需要它们。
答案 4 :(得分:0)
如果为B定义析构函数,则会调用它,然后是A。请参阅example at the bottom of the link you provided。
答案 5 :(得分:0)
好吧,我不知道析构函数,但你有其他有用的方法可以清理IDisposable中的Finalize()和Dispose()。
答案 6 :(得分:0)
快速控制台应用程序可以帮助测试这种情况。
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