终结器(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,而且你也不能保证垃圾回收器在退出前调用了所有终结器,所以必须使用 Dispose
或 DisposeAsync
来确保资源得到释放。
使用终结器释放资源
一般来说,对于开发人员,C# 所需的内存管理比不面向带垃圾回收的运行时的语言要少。 这是因为 .NET 垃圾回收器会隐式管理对象的内存分配和释放。 但是,如果应用程序封装非托管的资源,例如窗口、文件和网络连接,则应使用终结器释放这些资源。 当对象符合终止条件时,垃圾回收器会运行对象的 Finalize
方法。
显式释放资源
如果应用程序正在使用昂贵的外部资源,我们还建议在垃圾回收器释放对象前显式释放资源。 若要释放资源,请从 IDisposable 接口实现 Dispose
方法,对对象执行必要的清理。 这样可大大提高应用程序的性能。 如果调用 Dispose
方法失败,那么即使拥有对资源的显式控制,终结器也会成为清除资源的一个保障。
有关清除资源的详细信息,请参阅以下文章:
- 清理未托管资源(清理未托管资源)
- 实现 Dispose 方法
- 实现 DisposeAsync 方法
using
语句
示例
以下示例创建了三个类,并且这三个类构成了一个继承链。 类 First
是基类,Second
派生自 First
,Third
派生自 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.
*/