初始化及終止元件
您的元件是由其建構函式 (Visual Basic 中的 Sub New) 初始化並由其解構函式 (Visual Basic 中的 Sub Finalize) 終結。 您元件的建構函式是在建立元件的執行個體時呼叫,而其後就無法再呼叫建構函式。 而解構函式則是在記憶體回收終結您元件之前呼叫,接著就回收其記憶體。
Visual Basic 注意事項 |
---|
在舊版的 Visual Basic 中,Initialize 和 Terminate 事件與建構函式和解構函式的功能相同。 |
等候記憶體回收
在記憶體回收判斷元件已無法由任何執行程式碼取得之後,Common Language Runtime 會呼叫您元件的解構函式。 這會發生在以下兩種狀況中:如果所有的元件參考都已釋放,或是如果您元件的唯一參考是由同樣被所有執行程式碼隔離的物件所持有,例如發生在循環參考的情況中。
由於在使用者結束使用您的元件時與在呼叫其解構函式時兩者之間可能出現延遲,因此便在 .NET Framework 元件的存留期 (Lifetime) 中引入另一項步驟:如果您的元件要取得系統資源,例如資料庫連接或 Windows 系統物件控制代碼,則您應該實作 IDisposable 介面並提供 Dispose 方法,如此您元件的使用者就可以選擇何時釋放這些資源。
元件的生命週期
型別初始化:當您元件的第一個執行個體建立時,執行的第一個程式碼就是任何共用的初始程式碼。 任何共用成員的參考也會使得共用建構函式執行。 這包括任何初始化的共用欄位 (成員變數) 以及任何存在的共用建構函式 (Shared Sub New)。 下列程式碼為整個類別建立參考字型。
注意事項 |
---|
對應 Shared 的 C# 關鍵字是 static,請不要與 Visual Basic 中的 Static 關鍵字混淆。 |
Public Class ADrawing
Shared ReadOnly ReferenceFont As New Font("TimesNewRoman", 14)
' Shared constructor does not overload other constructors.
Shared Sub New()
' There is no call to the base class's shared constructor.
' Insert code to initialize the shared data.
End Sub
End Class
class ADrawing
{
static Font m_referenceFont = new Font("TimesNewRoman", 14);
// Simply add the static keyword to create a static constructor.
// Static constructors do not have any parameters.
static ADrawing()
{
// There is no call to the base class's static constructor.
// Code to initialize the font here.
}
}
注意事項 |
---|
即使未建立元件的執行個體,也可能發生類別初始化。 例如,具有共用成員函式的 abstract (MustInherit) 類別將會初始化,而即使未建立類別的執行個體,那些函式仍將提供應用程式使用。 |
執行個體初始化:當元件的執行個體建立時,會初始化擁有初始化程式碼的資料成員,並且執行適當的建構函式多載。 下列程式碼會初始化私用欄位,並且定義兩個建構函式,一個會在沒有參數時呼叫,另一個則在使用者指定參數時呼叫。
Class AShape Private answer As Integer = 42 Public Sub New() ' Call another constructor with default initialization values. MyClass.New(System.Drawing.Color.Red, 5280, DefinedSizes.Large) End Sub Public Overloads Sub New(myColor As Color, myLength As Integer, _ Size As DefinedSizes) ' Insert code to initialize the class. End Sub ' Defines the DefinedSizes enum Public Enum DefinedSizes Large Small End Enum End Class
class AShape { private int m_answer = 42; // Forward to another constructor. public AShape() : this(System.Drawing.Color.Red, 5280, DefinedSizes.Large) { // Additional initialization goes here. } public AShape(Color color, int length, DefinedSizes size) { // Code to initialize the class goes here. } // Defines the DefinedSizes enum public enum DefinedSizes { Large, Small } }
處置資源:如果您的元件實作 IDisposable 介面,則必須提供用戶端使用元件完畢時應該呼叫的 Dispose 方法。 請注意,任何繼承自 Component 的元件都已經有預設的 Dispose 實作,您可以覆寫該實作來提供額外的清除程式碼。 在 Dispose 方法中,您的元件會釋出所有可能已配置的系統資源、釋出其他物件的參考,以及讓自己呈現無法使用的狀態。 另外也可能有些情況適合元件呼叫自己擁有的 Dispose 方法。 下列程式碼會處置擁有 Dispose 方法的相依物件。
' Assumes that the class implements IDisposable Public Sub Dispose() Implements IDisposable.Dispose myWidget.Dispose myWidget = Nothing ' Insert additional code. End Sub
// Assumes that the class implements IDisposable public void IDisposable.Dispose() { mywidget.Dispose(); mywidget = null; // Dispose of remaining objects. }
呼叫 Dispose 之後,您的用戶端應該釋放所有剩餘的元件參考,如此記憶體回收才能回收元件的記憶體。
執行個體解構:當記憶體回收偵測到沒有剩餘的元件參考時,執行階段會呼叫您元件的解構函式 (在 Visual Basic 中為 Finalize),並且釋放記憶體。 您應該覆寫基底類別的 Finalize 方法 (適用 Visual Basic) 或實作解構函式 (適用 Visual C#),以實作自己的清除程式碼,但務必要包含解構函式的呼叫或基底類別的 Finalize 方法。
Protected Overrides Sub Finalize() m_Gadget = Nothing m_Gear = Nothing MyBase.Finalize() End Sub
// In C#, a destructor is used instead of a Finalize method. ~ThisClass() { m_gadget = null; m_gear = null; // The base class finalizer is called automatically }
應該實作 Dispose 方法的時機
如果您的元件繼承自 Component,則會提供 Dispose 的預設實作。 這個實作可覆寫來提供自訂清除程式碼。 如果您是藉由建立 IComponent 的自訂實作來建立您的元件,則應實作 IDisposable 來為您的元件提供 Dispose 方法。
如果您的元件要配置系統物件、資料庫連接或是使用者在一使用完元件就應釋放的其他稀有資源,則您的元件就需要 Dispose 方法。
此外,如果您的元件持有其他具有 Dispose 方法的物件參考,您也應該實作 Dispose 方法。
實作 Dispose 的原因
在使用者結束使用您元件時與記憶體回收偵測到無法取得元件的程式碼之際,依據系統活動的不同,兩者之間可能會出現無法預期的間隔。 如果您不提供 Dispose 方法,您的元件就會在此間隔期間繼續持有其資源。
最壞情況的案例
試想一下,現在有個使用資料庫連接但沒有 Dispose 方法的伺服程式元件。 在有著大量記憶體的伺服器上,您可能會建立並釋放元件的許多執行個體,而不會對可用的記憶體造成太大的影響。 在這種情況下,在釋放出元件參考之後的一段期間,記憶體回收可能尚未終結元件。
最後,所有可用的資料庫連接可能會因已釋放但尚未終結的元件而受阻。 即使伺服器具有足夠的記憶體,它可能還是無法回應使用者要求。