終結器 (舊稱為解構函式) 在垃圾回收器收集類別執行個體時,用來執行任何必要的清理工作。 在大部分情況下,您可以使用 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 方法。
警告
請勿從終結器存取受管理物件成員。 在完成期間,受管理物件可能已經處置,使其無法使用或處於無效狀態。 只能直接從終結器存取未受管理的資源。
明確釋放資源
如果您的應用程式使用昂貴的外部資源,我們建議您提供一種方法,在垃圾收集器釋放物件之前明確釋放該資源。 若要釋放資源,實作介面中的Dispose方法,以進行物件的必要清理。 這可以大幅改善應用程式效能。 即使對資源有明確控制,如果 Dispose 方法呼叫失敗,終結器還是會充當資源清理的保護措施。
如需清除資源的詳細資訊,請參閱下列文章:
範例
下列範例會建立三個產生繼承鏈結的類別。
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.
*/