在C#中,类中的析构函数和Finalize方法之间有什么区别?


97

类中的析构函数和Finalize方法之间有什么区别(如果有)?

我最近发现Visual Studio 2008将析构函数视为Finalize方法的同义词,这意味着Visual Studio不允许您同时在类中定义这两个方法。

例如,以下代码片段:

class TestFinalize
{
    ~TestFinalize()
    {
        Finalize();
    }

    public bool Finalize()
    {
        return true;
    }
}

调用析构函数中的Finalize时出现以下错误:

该调用在以下方法或属性之间是不明确的:'​​TestFinalize。〜TestFinalize()'和'TestFinalize.Finalize()'

并且,如果对Finalize的调用已被注释掉,则会出现以下错误:

类型“ ManagementConcepts.Service.TestFinalize”已经定义了具有相同参数类型的名为“ Finalize”的成员

Answers:


68

C#中的析构函数重写System.Object.Finalize方法。您必须使用析构函数语法。手动覆盖Finalize将给您一条错误消息。

基本上,您要使用Finalize方法声明进行的操作是隐藏基类的方法。这将导致编译器发出警告,可以使用new修饰符将其消除(如果它可以工作)。这里要注意的重要一点是,你不能两者override并声明new在同一时间以相同的名称构件,从而既具有析构函数和Finalize方法会导致错误(但你可以,但不建议使用,声明public new void Finalize()如果方法您并未声明析构函数)。


71

Wikipedia 在终结器文章中对终结器析构函数之间的区别进行了很好的讨论。

C#确实没有“ true”析构函数。语法类似于C ++析构函数,但实际上是终结器。您在示例的第一部分中正确编写了它:

~ClassName() { }

以上是Finalize功能的语法糖。它可以确保基础程序中的终结器能够运行,但在其他方面与覆盖Finalize函数相同。这意味着在编写析构函数语法时,实际上是在编写终结器。

根据Microsoft的说法,终结器是指垃圾收集器在收集(Finalize)时调用的函数,而析构函数是您所执行的一部分代码(成为的语法糖Finalize)。它们是如此接近同一件事,以至于Microsoft永远都不会做出区分。

微软对C ++的“析构函数”一词的使用具有误导性,因为在C ++中,一旦对象被删除或从堆栈中弹出,它就在同一线程上执行;而在C#中,它在另一时间在单独的线程上执行。


我认为,析构函数和终结器之间的这种区别是很重要的。但是,只有那些关心引擎盖下发生的事情的人才会关心上述区别。
凯尔·巴兰

1
还要注意,ECMA-334在很早以前就已经正式明确消除了“析构函数”和“终结器”的歧义。我不知道为什么MS仍坚持其规格中的误导性用语。
FrankHB

至少通过使用Mono,C#实际上是在C ++之后建模的,大多数本机C#对象都是C ++对象。编译Mono的编译器的工作方式决定了如何销毁这些C ++对象,以及同样地,如何将C#对象最终确定传播到C ++并调用那些析构函数。这种区别在幕后是有道理的,但实际上仍然不适用于C#本身。
Kenzi

20

在这里找到:http://sanjaysainitech.blogspot.com/2007/06/difference-between-destructor-dispose.html

  1. 析构函数

    它们是特殊的方法,其中包含对象的清理代码。您不能在代码中显式调用它们,因为它们是由GC隐式调用的。在C#中,它们的名称与带有~符号的类名相同。喜欢-

    Class MyClass
    {
    
    ~MyClass()
    {
    .....
    }
    }
    

    在VB.NET中,通过覆盖System.Object类的Finalize方法来实现析构函数。

  2. 处理

    这些方法与该类中的任何其他方法一样,可以显式调用,但是它们具有清理对象的特殊目的。在dispose方法中,我们为对象编写清理代码。重要的是,释放dispose方法中的所有非托管资源(如数据库连接,文件等)。实现dispose方法的类应实现IDisposable接口。Dispose方法应针对要处理的对象调用GC.SuppressFinalize方法,如果类具有破坏器,因为它已经完成了清理对象的工作,因此垃圾回收器不必调用对象的Finalize方法。参考:http : //msdn2.microsoft.com/en-us/library/aa720161(VS.71).aspx

  3. 完成

    如果未调用Dispose方法,则Finalize方法可作为清理资源的保障。您应该只实现Finalize方法来清理非托管资源。您不应为托管对象实现Finalize方法,因为垃圾收集器会自动清除托管资源。GC隐式调用Finalize方法,因此无法从代码中调用它。

    注意:在C#中,不能覆盖Finalize方法,因此必须使用其内部实现将覆盖MSIL中的Finalize方法的析构函数。但是在VB.NET中,可以覆盖Finalize方法,因为它确实支持析构函数方法。

更新: 有趣的半相关线程在这里


1
You should only implement a Finalize method to clean up unmanaged resources:您将其放在Finalize中。与“处置”相同吗?
hqt 2012年

@hqt:Dispose应该实施终结器的情况远远多于应该实施终结器的情况。实现Dispose,如果它是可能的类或派生类的实例将是最后一件事要么直接拥有的非托管资源,或直接持有的最后一件事直接拥有的非托管资源,或直接持有的最后一件事情,直接自己等等。只有Finalize在一个人的类<i>直接</ i>拥有一个不受管理的资源<i>而几乎没有其他东西</ i>的情况下,才实现资源清理–这是一个狭窄得多的方案。
2012年

@hqt:如果一个类将直接拥有非托管资源并且还持有对其他对象的引用,那么通常应将非托管资源拆分成自己的finalizable类(理想情况下,该类不应持有对其他任何对象的任何强引用)拥有对其他对象的引用的所有者将仅拥有“直接拥有非托管资源的东西”,而不是拥有资源本身,因此不需要终结器。
超级猫
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.