完成項 (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 垃圾收集行程會隱含地管理物件的記憶體配置和釋放。 不過,當您的應用程式封裝非受控資源,例如 windows、檔案和網路連線時,您應該使用完成項來釋放這些資源。 適合完成物件時,記憶體回收行程會執行物件的 Finalize
方法。
明確釋放資源
如果您的應用程式使用過多的外部資源,則也建議您提供一種方式,以在記憶體回收行程釋放物件之前明確釋放資源。 若要釋放資源,請從 IDisposable 介面實 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.
*/