终结器(C# 编程指南)

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

备注

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

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

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

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

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 队列中创建一个条目。 此队列由垃圾回收器处理。 当 GC 处理队列时,它会调用每个终结器。 不必要的终结器(包括空的终结器、仅调用基类终结器的终结器,或者仅调用条件性发出的方法的终结器)会导致不必要的性能损失。

程序员无法控制何时调用终结器,因为这由垃圾回收器决定。 垃圾回收器检查应用程序不再使用的对象。 如果它认为某个对象符合终止条件,则调用终结器(如果有),并回收用来存储此对象的内存。 可以通过调用 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# 语言规范中的终结器部分。

另请参阅