C# 託管資源與非託管資源(參考六)
類例項經常封裝對不受執行庫管理的資源(如視窗控制代碼 (HWND)、資料庫連線等)的控制。因此,應該既提供顯式方法也提供隱式方法來釋放這些資源。通過在物件上實現受保護的Finalize(在 C# 和 C++ 中為解構函式語法)可提供隱式控制。當不再有任何有效的物件引用後,垃圾回收器在某個時間呼叫此方法。
在有些情況下,您可能想為使用該物件的程式設計師提供顯式釋放這些外部資源的能力,以便在垃圾回收器釋放該物件之前釋放這些資源。當外部資源稀少或者昂貴時,如果程式設計師在資源不再使用時顯式釋放它們,則可以獲得更好的效能。若要提供顯式控制,需實現
注意,即使在通過Dispose提供顯式控制時,也應該使用Finalize方法提供隱式清理。Finalize提供了候補手段,可防止在程式設計師未能呼叫Dispose時造成資源永久洩漏。
有關如何實現Finalize和Dispose以清理非託管理資源的更多資訊,請參見垃圾回收。下面的程式碼示例演示實現Dispose的基本設計方案。此示例需要System名稱空間。
VB‘ 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
有關闡釋Finalize和Dispose的實現設計方案的更詳細的程式碼示例,請參見實現 Dispose 方法。
自定義釋放方法名稱
有時特定於域的名稱比Dispose更合適。例如,檔案封裝可能需要使用Close方法名稱。在此情況下,可以通過專用方式實現Dispose並建立呼叫Dispose的公共Close方法。下面的程式碼示例闡釋了這種模式。可以用適合您的域的方法名稱替換Close。此示例需要System名稱空間。
VB‘ 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
Finalize
下面的規則概括了Finalize方法的使用準則:
-
僅在要求終結的物件上實現Finalize。存在與Finalize方法相關的效能開銷。
-
如果需要Finalize方法,應考慮實現IDisposable,以使類的使用者可以避免因呼叫Finalize方法而帶來的開銷。
-
不要提高Finalize方法的可見性。該方法的可見性應該是protected,而不是public。
-
物件的Finalize方法應該釋放該物件擁有的所有外部資源。此外,Finalize方法應該僅釋放由該物件控制的資源。Finalize方法不應該引用任何其他物件。
-
不要對不是物件的基類的物件直接呼叫Finalize方法。在 C# 程式語言中,這不是有效的操作。
-
應在物件的Finalize方法中呼叫基類的 Finalize方法。
表 2 說明: 基類的Finalize方法通過 C# 和 C++ 解構函式語法自動進行呼叫。
釋放
下面的規則概括了Dispose方法的使用準則:
-
在封裝明確需要釋放的資源的型別上實現釋放設計方案。使用者可以通過呼叫公共Dispose方法釋放外部資源。
-
在通常包含控制資源的派生型別的基型別上實現釋放設計方案,即使基型別並不需要也如此。如果基型別有Close方法,這通常指示需要實現Dispose。在這類情況下,不要在基型別上實現Finalize方法。應該在任何引入需要清理的資源的派生型別中實現Finalize。
-
使用型別的Dispose方法釋放該型別所擁有的所有可釋放資源。
-
對例項呼叫了Dispose後,應通過呼叫GC.SuppressFinalize禁止Finalize方法執行。此規則的一個例外是當必須用Finalize完成Dispose沒有完成的工作的情況,但這種情況很少見。
-
如果基類實現了IDisposable,則應呼叫基類的Dispose方法。
-
不要假定Dispose將被呼叫。如果Dispose未被呼叫,也應該使用Finalize方法釋放型別所擁有的非託管資源。
-
當資源已經釋放時,在該型別上從例項方法(非Dispose)引發一個ObjectDisposedException。該規則不適用於Dispose方法,該方法應該可以在不引發異常的情況下被多次呼叫。
-
通過基型別的層次結構傳播對Dispose的呼叫。Dispose方法應釋放由此物件以及此物件所擁有的任何物件所控制的所有資源。例如,可以建立一個類似 TextReader 的物件來控制 Stream 和 Encoding,兩者均在使用者不知道的情況下由 TextReader 建立。另外,Stream 和 Encoding 都可以獲取外部資源。當對 TextReader 呼叫Dispose方法時,TextReader 應繼而對 Stream 和 Encoding 呼叫Dispose,使它們釋放其外部資源。
-
考慮在呼叫了某物件的Dispose方法後禁止對該物件的使用。重新建立已釋放的物件是難以實現的方案。
-
允許Dispose方法被呼叫多次而不引發異常。此方法在首次呼叫後應該什麼也不做。