實作 Finalize 和 Dispose 以清除 Unmanaged 資源
注意事項 |
---|
如需使用 C++ 的最終化與處置的詳細資訊,請參閱 Destructors and Finalizers in Visual C++。 |
類別執行個體通常透過非執行階段所管理的資源,例如視窗控制代碼 (HWND)、資料庫連接等等,來封裝控制項。因此,您應該提供明確和隱含的方式來釋放這些資源。藉由在物件上實作受保護的 Finalize 方法來提供隱含控制 (C# 和 C++ 中為解構函式語法)。在不再有任何有效的物件參考之後,記憶體回收行程會某些時間點上呼叫這個方法。
在某些情況下,您可能想要提供程式設計人員在記憶體回收行程釋放物件之前,使用能夠明確釋放這些外部資源的物件。如果外部資源不足或太過消耗,若程式設計人員明確釋放不再被使用的資源,會達到更佳的效能。若要提供明確控制,請實作由 IDisposable 介面所提供的 Dispose 方法。物件的消費者應該在完成此物件的使用時呼叫此方法。即使此物件的其他參考仍然存在,還是可以呼叫 Dispose。
請注意,即使當您使用 Dispose 來提供明確控制時,也應該使用 Finalize 方法提供隱含清除。如果程式設計人員呼叫 Dispose 失敗,則 Finalize 會提供備份,以避免資源被永久遺漏。
如需實作 Finalize 和 Dispose 來清除 Unmanaged 資源的詳細資訊,請參閱記憶體回收。下列程式碼範例將說明用於實作 Dispose 的基本設計模式,這個範例需要 System 命名空間。
' Design pattern for a base class.
Public Class Base
Implements IDisposable
' Field to handle multiple calls to Dispose gracefully.
Dim disposed as Boolean = false
' Implement IDisposable.
Public Overloads Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
Protected Overloads Overridable Sub Dispose(disposing As Boolean)
If disposed = False Then
If disposing Then
' Free other state (managed objects).
disposed = True
End If
End If
' Free your own state (unmanaged objects).
' Set large fields to null.
End Sub
Protected Overrides Sub Finalize()
' Simply call Dispose(False).
Dispose (False)
End Sub
End Class
' Design pattern for a derived class.
Public Class Derived
Inherits Base
' Field to handle multiple calls to Dispose gracefully.
Dim disposed as Boolean = false
Protected Overloads Overrides Sub Dispose(disposing As Boolean)
If disposed = False Then
If disposing Then
' Release managed resources.
disposed = True
End If
End If
' Release unmanaged resources.
' Set large fields to null.
' Call Dispose on your base class.
Mybase.Dispose(disposing)
End Sub
' The derived class does not have a Finalize method
' or a Dispose method without parameters because it inherits
' them from the base class.
End Class
// Design pattern for a base class.
public class Base: IDisposable
{
//Implement IDisposable.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// Free other state (managed objects).
}
// Free your own state (unmanaged objects).
// Set large fields to null.
}
// Use C# destructor syntax for finalization code.
~Base()
{
// Simply call Dispose(false).
Dispose (false);
}
}
// Design pattern for a derived class.
public class Derived: Base
{
protected override void Dispose(bool disposing)
{
if (disposing)
{
// Release managed resources.
}
// Release unmanaged resources.
// Set large fields to null.
// Call Dispose on your base class.
base.Dispose(disposing);
}
// The derived class does not have a Finalize method
// or a Dispose method without parameters because it inherits
// them from the base class.
}
如需可說明實作 Finalize 和 Dispose 的設計模式之詳細程式碼範例,請參閱 實作 Dispose 方法。
自訂 Dispose 方法名稱
有時候定義域特定的名稱要比 Dispose 更適合。例如,檔案封裝可能要使用方法名稱 Close。在這種情況下,會私用地實作 Dispose,以及建立可呼叫 Dispose 的公用 Close 方法。下列程式碼範例說明這個模式。您可以用適合於範圍的名稱取代 Close。這個範例需要 System 命名空間。
' Do not make this method overridable.
' A derived class should not be allowed
' to override this method.
Public Sub Close()
' Call the Dispose method with no parameters.
Dispose()
End Sub
// Do not make this method virtual.
// A derived class should not be allowed
// to override this method.
public void Close()
{
// Call the Dispose method with no parameters.
Dispose();
}
Finalize
下列規則概述 Finalize 方法的用法方針:
只在需要最終化的物件上實作 Finalize。有與 Finalize 方法關聯的效能成本。
如果您需要 Finalize 方法,應考慮實作 IDisposable,讓類別的使用者得以避免發生叫用 Finalize 方法的成本。
不要使 Finalize 方法更加顯露。它應該是 protected,而非 public。
物件的 Finalize 方法應該釋放其擁有的任何外部資源。此外,Finalize 方法應該只釋放物件所包含的資源。Finalize 方法不應該參考任何其他物件。
除了物件的基底類別之外,不要直接在物件上呼叫 Finalize。這不是 C# 程式語言的有效作業。
從物件的 Finalize 方法呼叫基底類別的 Finalize 方法。
注意事項 會使用 C# 和 C++ 解構函式語法來自動呼叫基底類別的 Finalize 方法。
處置
下列規則概述 Dispose 方法的使用方針:
在型別上實作處置設計模式,該型別會封裝需要明確釋放的資源。使用者可以呼叫公用 Dispose 方法來釋放外部資源。
在基底型別上實作處置設計模式,此型別一般都有包含資源的衍生型別,即使當基底型別並沒有時亦然。如果基底型別有 Close 方法,則通常表示需要實作 Dispose。在這種情況下,不要在基底型別上實作 Finalize 方法。Finalize 應該在任何引入需要清除的資源的衍生型別上被實作。
釋放型別在其 Dispose 方法中所擁有的任何可處置資源。
已經在執行個體上呼叫 Dispose 之後,藉由呼叫 GC.SuppressFinalize 方法來避免 Finalize 方法的執行。這個規則的例外情況是必須在不是 Dispose 所涵蓋的 Finalize 中完成工作的罕見狀況。
如果基底類別會實作 IDisposable,則呼叫它的 Dispose 方法。
不要假設 Dispose 將會被呼叫。型別所擁有的 Unmanaged 資源也應該在不呼叫 Dispose 的事件之 Finalize 方法中被釋放。
當資源已被處置時,從這個型別的執行個體方法 (除了 Dispose 以外) 擲回 ObjectDisposedException 。這個規則不適用於 Dispose 方法,因為這個方法應該可以被呼叫多次而不擲回例外狀況。
透過基底型別的階層架構將呼叫傳播到 Dispose。Dispose 方法應該釋放這個物件所包含的全部資源,以及這個物件所擁有的任何物件。例如,您可以建立類似 TextReader 的物件,其中包含 Stream 和 Encoding,兩者都是由 TextReader 所建立,而不需要知道使用者。此外,Stream 和 Encoding 都可以取得外部資源。當您在 TextReader 上呼叫 Dispose 方法時,它應該依次在 Stream 和 Encoding 上呼叫 Dispose,使它們釋放其外部資源。
您應該考慮在呼叫某物件的 Dispose 方法之後,不允許使用該物件。重新建立已經被處置的物件是一種很難實作的模式。
允許呼叫 Dispose 方法多次,而不會擲回例外狀況。在第一次呼叫之後,方法應該不會做任何動作。
Portions Copyright 2005 Microsoft Corporation.All rights reserved.
Portions Copyright Addison-Wesley Corporation.All rights reserved.
如需設計方針的詳細資訊,請參閱由 Krzysztof Cwalina 和 Brad Abrams 所著,並由 Addison-Wesley 於 2005 年發行的「Framework 設計方針:可重複使用之 .NET 程式庫的慣例、慣用語法和模式」一書。
請參閱
參考
IDisposable.Dispose Method
Object.Finalize Method