物件存留期:物件的建立和終結
更新:2007 年 11 月
類別的執行個體 (Instance) 是一個物件,它是使用 New 關鍵字所建立。使用新物件之前,通常必須在此新物件上執行初始設定工作。通用初始設定工作包括開啟檔案、連接至資料庫、以及讀取登錄機碼 (Registry Key) 的值。Visual Basic 會使用名為「建構函式」(Constructor) (允許控制初始設定的特殊方法) 的程序,控制新物件的初始設定。
當物件離開範圍之後,會由 Common Language Runtime (CLR) 釋放。Visual Basic 會使用名為「解構函式」(Destructor) 的程序,控制系統資源的釋放作業。建構函式和解構函式可一起支援建立強固和可預測的類別庫。
Sub New 和 Sub Finalize
Visual Basic 中的 Sub New 和 Sub Finalize 程序會初始化及終結物件,它們取代了在 Visual Basic 6.0 和更早版本中所使用的 Class_Initialize 和 Class_Terminate 方法。Sub New 建構函式和 Class_Initialize 不同,它在建立類別時只能執行一次,並且只能從相同類別或衍生類別 (Derived Class) 之另一個建構函式的第一行程式碼明確地呼叫它。此外,Sub New 方法中的程式碼一律會在類別中的任何其他程式碼執行之前執行。如果您沒有對類別明確地定義 Sub New 程序,Visual Basic 2005 (含) 以後版本就會在執行階段隱含建立 Sub New 建構函式。
CLR 會在釋放物件之前自動為定義 Sub Finalize 程序的物件呼叫 Finalize 方法。Finalize 方法可包含當物件正要終結之前需要執行的程式碼,例如關閉檔案和儲存狀態資訊的程式碼。執行 Sub Finalize 會造成些微的效能損失,所以您應該只有在需要明確釋放物件時才定義 Sub Finalize 方法。
注意事項: |
---|
CLR 中的記憶體回收行程不會 (也不能) 處置 (Dispose) CLR 環境外的「Unmanaged 物件」(作業系統直接執行的物件)。這是因為不同的 Unmanaged 物件必須使用不同的方法處置。該資訊不會直接與 Unmanaged 物件相關聯,必須在物件的文件中尋找此資訊。使用 Unmanaged 物件的類別必須在 Finalize 方法中處置這些物件。 |
Finalize 解構函式是保護的 (Protected) 方法,只能從所屬的類別或衍生的類別中呼叫。當物件終結時,系統會自動呼叫 Finalize,所以您不應該從衍生類別的 Finalize 實作 (Implementation) 之外明確地呼叫 Finalize。
不同於在物件設為 nothing 時就立即執行的 Class_Terminate,在物件失去範圍和 Visual Basic 呼叫 Finalize 解構函式的兩個時間點之間通常會發生延遲。Visual Basic 2005 (含) 以後版本都允許可隨時明確呼叫的第二種解構函式 Dispose,以立即釋放資源。
注意事項: |
---|
Finalize 解構函式不應該擲回例外狀況 (Exception),因為應用程式無法處理這些例外狀況,而且它們會造成應用程式終止。 |
IDisposable 介面
類別執行個體通常負責控制非由 CLR 管理的資源,例如 Windows 控制代碼和資料庫連接。這些資源必須在類別的 Finalize 方法中加以處置,這樣才能在記憶體回收行程終結物件時釋放這些資源。不過,只有在 CLR 需要更多可用記憶體時,記憶體回收行程才會終結物件;這表示要等到物件超過範圍很久之後,資源才會釋放。
若要彌補記憶體回收行程之不足,如果類別實作 IDisposable 介面,則可以提供主動管理系統資源的機制。IDisposable 有一個方法 Dispose,當用戶端完成使用物件時應該呼叫此方法。您可以使用 Dispose 方法立即釋放資源,並執行像是關閉檔案和資料庫連接的工作。與 Finalize 解構函式不同的是,系統無法自動呼叫 Dispose 方法。當您要立即釋放資源時,類別的用戶端必須明確地呼叫 Dispose。
實作 IDisposable
實作 IDisposable 介面的類別應該包括這些程式碼區段:
持續追蹤是否已處置物件的欄位:
Protected disposed As Boolean = False
釋放類別資源的 Dispose 多載。此方法應該使用基底類別的 Dispose 和 Finalize 方法呼叫:
Protected Overridable Sub Dispose(ByVal disposing As Boolean) If Not Me.disposed Then If disposing Then ' Insert code to free managed resources. End If ' Insert code to free unmanaged resources. End If Me.disposed = True End Sub
僅包含下列程式碼的 Dispose 實作:
Public Sub Dispose() Implements IDisposable.Dispose Dispose(True) GC.SuppressFinalize(Me) End Sub
僅包含下列程式碼的 Finalize 方法覆寫:
Protected Overrides Sub Finalize() Dispose(False) MyBase.Finalize() End Sub
衍生自實作 IDisposable 的類別
若類別是衍生自實作 IDisposable 介面的基底類別,則不需要覆寫任何基底方法,除非它使用了需要處置的其他資源。在這個情形下,衍生類別應該覆寫基底類別的 Dispose(disposing) 方法,處置衍生類別的資源。這個覆寫作業必須呼叫基底類別的 Dispose(disposing) 方法。
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If Not Me.disposed Then
If disposing Then
' Insert code to free managed resources.
End If
' Insert code to free unmanaged resources.
End If
MyBase.Dispose(disposing)
End Sub
衍生類別不應該覆寫基底類別的 Dispose 和 Finalize 方法。從衍生類別的執行個體呼叫這些方法時,這些方法的基底類別實作會呼叫衍生類別的 Dispose(disposing) 方法覆寫。
視覺化
下圖顯示要繼承的方法,以及要在衍生類別中覆寫的方法。
遵循這個 DisposeFinalize 模式時,衍生類別和基底類別的資源就會得到正確的處置。下圖顯示處置和完成類別時要呼叫的方法。
記憶體回收和 Finalize 解構函式
.NET Framework 會使用「參考追蹤記憶體回收」系統,定期釋放未使用的資源。Visual Basic 6.0 和之前的版本使用另一種名為「參考計數」的系統管理資源。雖然兩套系統都自動執行相同的功能,但仍有些重要的差異。
當系統決定不再需要某些物件時,CLR 就會定期終結它們。物件會因為系統資源短缺而加快釋放的速度,如果系統資源夠用,則會降低釋放的頻率。物件失去範圍和 CLR 釋放它之間的延遲是指您無法精確地判斷終結物件的時間,而這與 Visual Basic 6.0 與舊版的物件不同。在這種情況下,就是指物件具有「不具決定性存留期」。在大部分情況下,只要您記住 Finalize 解構函式在物件失去範圍時不能立即執行,不具決定性存留期就不會變更撰寫應用程式的方式。
這兩種記憶體回收系統之間的另一個差異牽涉 Nothing 的使用。為了在 Visual Basic 6.0 和之前的版本中利用參考計數,程式設計人員有時會將 Nothing 指派給物件變數,以便釋放這些變數保留的參考。如果變數保留物件最後的參考,物件資源就會立即釋放。在新版 Visual Basic 中,雖然有時此程序仍相當重要,執行此程序絕不會造成參考的物件立即釋放其資源。若要立即釋放資源,請使用物件的 Dispose 方法 (可用的話)。您必須將變數設為 Nothing 的唯一時機,是其存留期在與記憶體回收行程偵測失去關聯之物件所花費的時間相較之下,存留期會比較長時才需設定。
請參閱
工作
HOW TO:實作 Dispose Finalize 模式 (Visual Basic)