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

114

在一个类中,析构函数和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”具有相同参数类型的成员。

3个回答

76

C# 中的析构函数要覆盖 System.Object.Finalize 方法。你必须使用析构函数语法来实现。手动覆盖 Finalize 方法会导致错误消息。

基本上,你想要实现的是隐藏父类方法的功能,需要在 Finalize 方法声明中使用 hiding 关键字。这将导致编译器发出警告,可以通过使用 new 修饰符来消除警告(如果这种方式可行)。需要注意的重要一点是,你不能同时既使用 override 又声明一个具有相同名称的 new 成员,因此在同时使用析构函数和 Finalize 方法时会导致错误(但是如果不声明析构函数,则可以但不建议声明一个 public new void Finalize() 方法)。


76

维基百科上有一些关于finalizer文章中终结器和destructor之间区别的讨论。

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

~ClassName() { }

上述内容是一个Finalize函数的语法糖。它确保了基类中的终结器得到保证运行,但除此之外与重写Finalize函数完全相同。这意味着当你编写析构函数语法时,实际上是在编写终结器。

根据微软公司的说法, 终结器是指垃圾回收器在回收时调用的函数(Finalize),而析构函数是作为结果执行的代码片段(成为Finalize的语法糖)。它们非常接近于相同的东西,以至于微软公司本不应该做出区分。

微软公司对C++中“析构函数”的使用方式是具有误导性的,因为在C++中,它会在对象被删除或从堆栈中弹出时立即在同一线程上执行,而在C#中则会在另一个时间在单独的线程上执行。


我认为析构函数和终结器之间的区别是很重要的。不过,只有关心底层情况的人才会在意这种区别。 - Kyle Baran
1
另外请注意,ECMA-334很久以前就明确区分了“析构函数”和“终结器”。我不知道为什么微软在他们的规范中仍然坚持使用这个具有误导性的术语。 - FrankHB
至少从使用Mono的经验来看,C#实际上是基于C++建模的,大多数本地C#对象都是C++对象。编译Mono的编译器的工作方式决定了这些C++对象如何被销毁,同样,C#对象的最终化是如何向下传播到C++并调用那些析构函数的。这种区别在底层是有意义的,但它仍然不适用于C#本身。 - Kenzi

21

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

  1. Destructor

    They are special methods that contains clean up code for the object. You can not call them explicitly in your code as they are called implicitly by GC. In C# they have same name as the class name preceded by the ~ sign. Like-

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

    In VB.NET, destructors are implemented by overriding the Finalize method of the System.Object class.

  2. Dispose

    These are just like any other methods in the class and can be called explicitly but they have a special purpose of cleaning up the object. In the dispose method we write clean up code for the object. It is important that we freed up all the unmanaged recources in the dispose method like database connection, files etc. The class implementing dispose method should implement IDisposable interface.A Dispose method should call the GC.SuppressFinalize method for the object it is disposing if the class has desturctor because it has already done the work to clean up the object, then it is not necessary for the garbage collector to call the object's Finalize method. Reference: http://msdn2.microsoft.com/en-us/library/aa720161(VS.71).aspx

  3. Finalize

    A Finalize method acts as a safeguard to clean up resources in the event that your Dispose method is not called. You should only implement a Finalize method to clean up unmanaged resources. You should not implement a Finalize method for managed objects, because the garbage collector cleans up managed resources automatically. Finalize method is called by the GC implicitly therefore you can not call it from your code.

    Note: In C#, Finalize method can not be override, so you have to use destructor whose internal implementation will override the Finalize method in MSIL.But in the VB.NET, Finalize method can be override because it does support destructor method.

更新:这里有一个有趣的半相关主题讨论串


1
你应该只实现一个 Finalize 方法来清理未受管理的资源:你把它放在 Finalize 里。Dispose 方法也一样吗? - hqt
@hqt:应该实现“Dispose”的情况远远超过应该实现finalizer的情况。如果一个类或派生类的实例很可能是直接拥有非托管资源的最后一件事,或者直接拥有最后一件直接拥有非托管资源的东西,或者直接拥有最后一件直接拥有等等的东西,则应该实现“Dispose”。仅在一个类<i>直接</i>拥有非托管资源<i>并且几乎没有其他内容</i>的情况下,才为资源清理实现“Finalize”——这是一个更狭窄的场景。 - supercat
如果一个类直接拥有非托管资源并且还持有对其他对象的引用,那么这些非托管资源通常应该被分离到它们自己的可终结类中(最好不要持有任何对其他对象的强引用)。这意味着持有对其他对象的引用的类只会拥有“直接拥有非托管资源的东西”,而不是拥有资源本身,因此不需要终结器。 - supercat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接