完成項 (舊稱為解構函式) 可在記憶體回收行程收集類別執行個體時,用來執行任何必要的最後清除。 在大部分情況下,您可以使用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 介面實作IDisposable 方法,以便對物件執行必要清除。 這可以大幅改善應用程式效能。 即使對資源使用這個明確控制,如果Dispose 方法呼叫失敗,完成項還是會成為清除資源的保護措施。
如需清除資源的詳細資訊,請參閱下列文章:
下列範例會建立三個產生繼承鏈結的類別。First 類別是基底類別、Second 衍生自First,而Third 衍生自Second。 所有這三個都有完成項。 在Main 中,會建立最高衍生性類別的執行個體。 此程式碼的輸出取決於應用程式設為目標的 .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.*/此頁面對您有幫助嗎?
需要本主題的協助嗎?
想要嘗試使用 Ask Learn 來釐清或引導您完成本主題嗎?
此頁面對您有幫助嗎?
想要嘗試使用 Ask Learn 來釐清或引導您完成本主題嗎?