终结器(C# 编程指南)

终结器(以前称为析构函数)用于在垃圾回收器收集类实例时执行任何必要的最终清理操作。 在大多数情况下,通过使用 System.Runtime.InteropServices.SafeHandle 或派生类包装任何非托管句柄,可以免去编写终结器的过程。

备注

  • 无法在结构中定义终结器。 它们仅用于类。
  • 一个类只能有一个终结器。
  • 不能继承或重载终结器。
  • 不能手动调用终结器。 可以自动调用它们。
  • 终结器不使用修饰符或参数。

例如,以下是类 Car 的终结器声明。

class Car
{
    ~Car()  // finalizer
    {
        // cleanup statements...
    }
}

终结器也可以作为表达式主体定义实现,如下面的示例所示。

using System;

public class Destroyer
{
   public override string ToString() => GetType().Name;

   ~Destroyer() => Console.WriteLine($"The {ToString()} finalizer is executing.");
}

终结器隐式调用对象基类上的 Finalize。 因此,对终结器的调用会隐式转换为以下代码:

protected override void Finalize()
{
    try
    {
        // Cleanup statements...
    }
    finally
    {
        base.Finalize();
    }
}

这种设计意味着,对继承链(从派生程度最高到派生程度最低)中的所有实例以递归方式调用 Finalize 方法。

备注

不应使用空终结器。 如果类包含终结器,会在 Finalize 队列中创建一个条目。 垃圾回收器调用终结器时,将处理此队列。 空终结器只会导致不必要的性能损失。

程序员无法控制何时调用终结器,因为这由垃圾回收器决定。 垃圾回收器检查应用程序不再使用的对象。 如果它认为某个对象符合终止条件,则调用终结器(如果有),并回收用来存储此对象的内存。 可以通过调用 Collect 强制进行垃圾回收,但多数情况下应避免此调用,因为它可能会造成性能问题。

备注

对于终结器是否在应用程序终止过程中运行,这特定于每个 .NET 的实现。 在应用程序终止时,.NET Framework 会尽一切合理努力为尚未被执行垃圾回收的对象调用终结器,除非此类清理操作已被禁止(例如,通过调用库方法 GC.SuppressFinalize)。 .NET 5(包括 .NET Core)及更高版本不会在应用程序终止过程中调用终结器。 有关详细信息,请参阅 GitHub 问题 dotnet/csharpstandard #291

如果需要在应用程序退出时确保清理操作能够可靠地执行,请为 System.AppDomain.ProcessExit 事件注册一个处理程序。 该处理程序将确保在应用程序退出之前,为所有需要执行清理操作的对象调用了 IDisposable.Dispose()(或 IAsyncDisposable.DisposeAsync())。 因为你不能直接调用 Finalize,而且你也不能保证垃圾回收器在退出前调用了所有终结器,所以必须使用 DisposeDisposeAsync 来确保资源得到释放。

使用终结器释放资源

一般来说,对于开发人员,C# 所需的内存管理比不面向带垃圾回收的运行时的语言要少。 这是因为 .NET 垃圾回收器会隐式管理对象的内存分配和释放。 但是,如果应用程序封装非托管的资源,例如窗口、文件和网络连接,则应使用终结器释放这些资源。 当对象符合终止条件时,垃圾回收器会运行对象的 Finalize 方法。

显式释放资源

如果应用程序正在使用昂贵的外部资源,我们还建议在垃圾回收器释放对象前显式释放资源。 若要释放资源,请从 IDisposable 接口实现 Dispose 方法,对对象执行必要的清理。 这样可大大提高应用程序的性能。 如果调用 Dispose 方法失败,那么即使拥有对资源的显式控制,终结器也会成为清除资源的一个保障。

有关清除资源的详细信息,请参阅以下文章:

示例

以下示例创建了三个类,并且这三个类构成了一个继承链。 类 First 是基类,Second 派生自 FirstThird 派生自 Second。 这三个类都具有终结器。 在 Main 中,已创建派生程度最高的类的一个实例。 此代码的输出取决于应用程序所面向的 .NET 实现:

  • .NET Framework:输出显示当应用程序终止时,这三个类的终结器将按照派生程度最高到最低的顺序自动进行调用。
  • .NET 5(包括 .NET Core)或更高版本:没有输出,因为在应用程序终止时,此 .NET 的实现不调用终结器。
class First
{
    ~First()
    {
        System.Diagnostics.Trace.WriteLine("First's finalizer is called.");
    }
}

class Second : First
{
    ~Second()
    {
        System.Diagnostics.Trace.WriteLine("Second's finalizer is called.");
    }
}

class Third : Second
{
    ~Third()
    {
        System.Diagnostics.Trace.WriteLine("Third's finalizer is called.");
    }
}

/* 
Test with code like the following:
    Third t = new Third();
    t = null;

When objects are finalized, the output would be:
Third's finalizer is called.
Second's finalizer is called.
First's finalizer is called.
*/

C# 语言规范

有关详细信息,请参阅 C# 语言规范中的析构函数部分。

请参阅