Share via


物件存留期:物件的建立和終結 (Visual Basic)

類別的執行個體 (Instance) 是一個物件,它是使用 New 關鍵字所建立。 使用新物件之前,通常必須在此新物件上執行初始設定工作。 通用初始設定工作包括開啟檔案、連接至資料庫、以及讀取登錄機碼 (Registry Key) 的值。 Visual Basic 會使用名為「建構函式」(Constructor) (允許控制初始設定的特殊方法) 的程序,控制新物件的初始設定。

當物件離開範圍之後,會由 Common Language Runtime (CLR) 釋放。 Visual Basic 會使用名為「解構函式」(Destructor) 的程序,控制系統資源的釋放作業。 建構函式和解構函式可一起支援建立強固和可預測的類別庫。

使用建構函式和解構函式

建構函式和解構函式控制物件的建立與解構。 Visual Basic 中的 Sub New 和 Sub Finalize 程序會初始化及終結物件,它們取代了在 Visual Basic 6.0 和更早版本中所使用的 Class_Initialize 和 Class_Terminate 方法。

Sub New

Sub New 建構函式只能在建立類別時執行一次。 並且只能從相同類別或衍生類別 (Derived Class) 之另一個建構函式的第一行程式碼明確地呼叫它。 此外,Sub New 方法中的程式碼一律會在類別中的任何其他程式碼執行之前執行。 如果您沒有對類別明確地定義 Sub New 程序,Visual Basic 2005 (含) 以後版本就會在執行階段隱含建立 Sub New 建構函式。

若要建立類別的建構函式,要先在類別定義中的任意處,建立名為 Sub New 的程序。 若要建立參數型建構函式,應將引數的名稱和資料型別指定給 Sub New,正如您為其他任何程序指定引數一般,如下列程式碼所示:

Sub New(ByVal s As String)

通常建構函式都是多載,如下列程式碼所示:

Sub New(ByVal s As String, i As Integer)

當您定義衍生自另一類別的類別時,建構函式的第一行必須呼叫基底類別的建構函式,除非基底類別含有不帶有任何參數的可存取建構函式。 例如,呼叫包含上述建構函式的基底類別可以是 MyBase.New(s)。 否則,MyBase.New 為選擇項,而且 Visual Basic 執行階段會隱含地呼叫它。

撰寫用於呼叫父物件建構函式的程式碼之後,您可以將任何額外的初始化程式碼加入至 Sub New 程序。 以參數化建構函式呼叫 Sub New 時,它可以接受引數。 這些參數會從呼叫建構函式的程序傳遞過來,例如 Dim AnObject As New ThisClass(X)。

Sub Finalize

CLR 會在釋放物件之前自動為定義 Sub Finalize 程序的物件呼叫 Finalize 方法。 Finalize 方法可包含當物件正要終結之前需要執行的程式碼,例如關閉檔案和儲存狀態資訊的程式碼。 執行 Sub Finalize 會造成些微的效能損失,所以您應該只有在需要明確釋放物件時才定義 Sub Finalize 方法。

注意事項注意事項

CLR 中的記憶體回收行程不會 (也不能) 處置 CLR 環境外的「Unmanaged 物件」(Unmanaged Object),此物件是由作業系統直接執行的。 這是因為不同的 Unmanaged 物件必須使用不同的方法處置。 該資訊不會直接與 Unmanaged 物件相關聯,必須在物件的文件中尋找此資訊。 使用 Unmanaged 物件的類別必須在 Finalize 方法中處置這些物件。

Finalize 解構函式是保護的方法,只能從所屬的類別或衍生的類別中呼叫。 當物件終結時,系統會自動呼叫 Finalize,所以您不應該從衍生類別的 Finalize 實作 (Implementation) 之外明確地呼叫 Finalize。

不像 Class_Terminate,它會在物件設為 Nothing 時立刻執行,而物件失去範圍和 Visual Basic 呼叫 Finalize 解構函式之間通常會有延遲。 Visual Basic 2005 (含) 以後版本允許使用第二種解構函式 Dispose,它可以隨時明確予以呼叫,以便立即釋放資源。

注意事項注意事項

Finalize 解構函式不應該擲回例外狀況 (Exception),因為應用程式無法處理這些例外狀況,而且它們會造成應用程式終止。

類別階層架構中的 New 和 Finalize 方法如何運作

每次建立類別的執行個體時,Common Language Runtime (CLR) 都會嘗試執行名為 New 的程序 (如果此程序存在該物件中的話)。 New 是一種稱為 constructor 的程序,用於在物件中任何其他程式碼執行之前初始化新物件。 New 建構函式可用來開啟檔案、連接至資料庫、初始化變數以及處理使用物件之前必須完成的任何其他工作。

當建立衍生類別的執行個體時,會先執行基底類別的 Sub New 建構函式,接著執行衍生類別中的建構函式。 因為 Sub New 建構函式中的第一行程式碼,會使用 MyBase.New() 語法來呼叫類別階層架構中緊接其上的類別建構函式,所以會產生前面的作業。 然後,針對類別階層架構中的每個類別來呼叫 Sub New 建構函式,直到達到基底類別的建構函式。 這時,會執行基底類別建構函式中的程式碼,接著執行所有衍生類別中每個建構函式中的程式碼,最後執行最高衍生類別中的程式碼。

建構函式和繼承

當不再需要物件時,CLR 會先呼叫該物件的 Finalize 方法,之後才會釋放其記憶體。 Finalize 方法稱為 destructor,因為它執行如儲存狀態資訊、關閉檔案和連接至資料庫的清除工作,而且在釋放物件前必須完成的其他工作。

建構函式和繼承 2

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) 方法覆寫。

記憶體回收和 Finalize 解構函式

.NET Framework 會使用「參考追蹤記憶體回收」(Reference-Tracing Garbage Collection) 系統,定期釋放未使用的資源。 Visual Basic 6.0 和之前的版本使用另一種名為「參考計數」(Reference Counting) 的系統管理資源。 雖然兩套系統都自動執行相同的功能,但仍有些重要的差異。

當系統決定不再需要某些物件時,CLR 就會定期終結它們。 物件會因為系統資源短缺而加快釋放的速度,如果系統資源夠用,則會降低釋放的頻率。 物件失去範圍和 CLR 釋放它之間的延遲是指您無法精確地判斷終結物件的時間,而這與 Visual Basic 6.0 與舊版的物件不同。 在這種情況下,就是指物件具有「不具決定性存留期」(Non-Deterministic Lifetime)。 在大部分情況下,只要您記住 Finalize 解構函式在物件失去範圍時不能立即執行,不具決定性存留期就不會變更撰寫應用程式的方式。

這兩種記憶體回收系統之間的另一個差異牽涉 Nothing 的使用。 為了在 Visual Basic 6.0 和之前的版本中利用參考計數,程式設計人員有時會將 Nothing 指派給物件變數,以便釋放這些變數保留的參考。 如果變數保留物件最後的參考,物件資源就會立即釋放。 在新版 Visual Basic 中,雖然有時此程序仍相當重要,執行此程序絕不會造成參考的物件立即釋放其資源。 若要立即釋放資源,請使用物件的 Dispose 方法 (可用的話)。 您必須將變數設為 Nothing 的唯一時機,是其存留期在與記憶體回收行程偵測失去關聯之物件所花費的時間相較之下,存留期會比較長時才需設定。

請參閱

參考

New 運算子 (Visual Basic)

Dispose

Nothing (Visual Basic)

概念

初始化及終止元件

Finalize 方法和解構函式