Objektlebensdauer: Erstellen und Zerstören von Objekten (Visual Basic)

Eine Instanz einer Klasse, ein Objekt, wird mit dem New-Schlüsselwort erstellt. Häufig müssen Initialisierungsaufgaben für neue Objekte ausgeführt werden, bevor sie verwendet werden. Zu den allgemeinen Initialisierungsaufgaben zählen das Öffnen von Dateien, das Herstellen von Verbindungen mit Datenbanken und das Lesen von Registrierungsschlüsselwerten. Visual Basic steuert die Initialisierung neuer Objekte mit Prozeduren, die als Konstruktoren bezeichnet werden (spezielle Methoden, mit denen die Initialisierung gesteuert werden kann).

Wenn sich ein Objekt nicht mehr im Gültigkeitsbereich befindet, wird es von der Common Language Runtime (CLR) freigegeben. Visual Basic steuert die Freigabe von Systemressourcen mit Prozeduren, die als Destruktoren bezeichnet werden. Konstruktoren und Destruktoren unterstützen gemeinsam die Erstellung stabiler und vorhersehbarer Klassenbibliotheken.

Verwenden von Konstruktoren und Destruktoren

Konstruktoren und Destruktoren steuern die Erstellung und Zerstörung von Objekten. Die Sub New-Prozedur und die Sub Finalize-Prozedur in Visual Basic initialisieren bzw. zerstören Objekte. Sie ersetzen die in Visual Basic 6.0 und früheren Versionen verwendete Class_Initialize-Methode und Class_Terminate-Methode.

Sub New

Der Sub New-Konstruktor kann nur einmal während der Erstellung der Klasse ausgeführt werden. Er kann nur von der ersten Codezeile eines anderen Konstruktors derselben oder einer abgeleiteten Klasse aus explizit aufgerufen werden und von nirgendwo sonst. Weiterhin wird der Code in der Sub New-Methode immer vor jedem anderen Code in einer Klasse ausgeführt. Visual Basic 2005 und höhere Versionen erstellen zur Laufzeit implizit einen Sub New-Konstruktor, wenn Sie nicht explizit eine Sub New-Prozedur für eine Klasse definieren.

Zum Erstellen des Konstruktors für eine Klasse erstellen Sie an beliebiger Stelle in der Klassendefinition eine Prozedur mit der Bezeichnung Sub New. Zum Erstellen eines parametrisierten Konstruktors legen Sie die Namen und Datentypen der Argumente von Sub New genauso fest, als wenn Sie die Argumente für eine beliebige andere Prozedur angeben würden. Beispiel:

Sub New(ByVal s As String)

Konstruktoren sind häufig überladen (siehe folgenden Code):

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

Wenn Sie eine von einer anderen Klasse abgeleitete Klasse definieren, muss die erste Zeile eines Konstruktors einen Aufruf an den Konstruktor der Basisklasse beinhalten, sofern die Basisklasse nicht über einen zugreifbaren Konstruktor verfügt, der keine Parameter benötigt. Ein Aufruf der Basisklasse mit dem oben stehenden Konstruktor wäre beispielsweise MyBase.New(s). Andernfalls ist MyBase.New optional und wird von der Visual Basic-Laufzeit implizit aufgerufen.

Nach dem Verfassen von Code zum Aufrufen des Konstruktors des übergeordneten Projekts kann der Sub New-Prozedur beliebiger zusätzlicher Initialisierungsode hinzugefügt werden. Sub New kann Argumente akzeptieren, wenn es als parametrisierter Konstruktor aufgerufen wird. Diese Parameter werden von der den Konstruktor aufrufenden Prozedur, z. B. Dim AnObject As New ThisClass(X), übergeben.

Sub Finalize

Vor der Freigabe von Objekten ruft die CLR automatisch die Finalize-Methode für Objekte auf, die eine Sub Finalize-Prozedur definieren. Die Finalize-Methode kann Code enthalten, der direkt vor der Zerstörung eines Objekts ausgeführt werden muss, z. B. Code zum Schließen von Dateien und Speichern von Zustandsinformationen. Durch Ausführung von Sub Finalize kommt es zu leichten Leistungseinbußen. Sie sollten deshalb nur dann eine Sub Finalize-Methode definieren, wenn Objekte explizit freigegeben werden müssen.

Tipp

Der Garbage Collector in der CLR gibt nicht verwaltete Objekte nicht frei (und kann sie nicht freigeben). Dies sind Objekte, die vom Betriebssystem direkt, außerhalb der CLR-Umgebung, ausgeführt werden. Der Grund hierfür ist, dass unterschiedliche nicht verwaltete Objekte auf verschiedene Weise freigegeben werden müssen. Diese Informationen sind den nicht verwalteten Objekten nicht direkt zugeordnet. Sie müssen in der Dokumentation des betreffenden Objekts gesucht werden. Eine Klasse, die nicht verwaltete Objekte verwendet, muss sie in ihrer Finalize-Methode freigeben.

Der Finalize-Destruktor ist eine geschützte Methode, die nur über die zugehörige Klasse oder über abgeleitete Klassen aufgerufen werden kann. Das System ruft Finalize automatisch auf, wenn ein Objekt zerstört wird. Sie sollten deshalb Finalize nicht explizit von außerhalb der Finalize-Implementierung einer abgeleiteten Klasse aufrufen.

Im Gegensatz zu Class_Terminate, das ausgeführt wird, sobald ein Objekt auf "nothing" festgelegt wird, tritt in der Regel eine Verzögerung zwischen dem Zeitpunkt, zu dem ein Objekt seinen Gültigkeitsbereich verliert, und dem Aufruf des Finalize-Destruktors durch Visual Basic auf. Visual Basic 2005 und höhere Versionen ermöglichen die Verwendung einer zweiten Art von Destruktor (Dispose), der zu jedem beliebigen Zeitpunkt explizit für die umgehende Freigabe von Ressourcen aufgerufen werden kann.

Tipp

Ein Finalize-Destruktor sollte keine Ausnahmen auslösen, da die Anwendung sie nicht behandeln kann und sie das Beenden der Anwendung verursachen können.

Wie funktionieren die Methoden "New" und "Finalize" in einer Klassenhierarchie?

Immer, wenn eine Instanz einer Klasse erstellt wird, versucht die Common Language Runtime (CLR), eine Prozedur mit dem Namen New auszuführen, wenn sie in diesem Objekt vorhanden ist. New ist eine Art von Prozedur, die als constructor bezeichnet wird, mit der neue Objekte vor Ausführung jedes anderen Codes in einem Objekt initialisiert werden. Ein New-Konstruktor kann zum Öffnen von Dateien, Verbinden mit Datenbanken, Initialisieren von Variablen sowie für andere Aufgaben verwendet werden, die ausgeführt werden müssen, bevor ein Objekt verwendet werden kann.

Wenn eine Instanz einer abgeleiteten Klasse erstellt wird, werden zuerst der Sub New-Konstruktor der Basisklasse und anschließend die Konstruktoren in den abgeleiteten Klassen ausgeführt. Dies geschieht, da die erste Codezeile in einem Sub New-Konstruktor die Syntax MyBase.New() verwendet, um den Konstruktor der Klasse aufzurufen, die sich in der Klassenhierarchie unmittelbar darüber befindet. Der Sub New-Konstruktor wird dann für jede Klasse in der Klassenhierarchie aufgerufen, bis der Konstruktor für die Basisklasse erreicht wird. Zu diesem Zeitpunkt wird der Code im Konstruktor für die Basisklasse, gefolgt vom Code in jedem Konstruktor in allen abgeleiteten Klassen, ausgeführt. Der Code in den am stärksten abgeleiteten Klassen wird zuletzt ausgeführt.

Konstruktoren und Vererbung

Wenn ein Objekt nicht mehr benötigt wird, ruft die CLR die Finalize-Methode für dieses Objekt auf, bevor der Speicher freigegeben wird. Die Finalize-Methode wird als destructor bezeichnet, da sie Bereinigungsaufgaben ausführt wie das Speichern von Zustandsinformationen, Schließen von Dateien und Verbinden mit Datenbanken sowie andere Aufgaben, die vor der Freigabe eines Objekts ausgeführt werden müssen.

Konstruktoren und Vererbung 2

IDisposable-Schnittstelle

Klasseninstanzen steuern oft nicht von der CLR verwaltete Ressourcen, z. B. Windows-Handles und Datenbankverbindungen. Diese Ressourcen müssen in der Finalize-Methode der Klasse freigegeben werden, sodass sie freigegeben werden, wenn das Objekt vom Garbage Collector zerstört wird. Der Garbage Collector zerstört Objekte jedoch nur, wenn die CLR mehr freien Arbeitsspeicher erfordert. Daher werden die Ressourcen möglicherweise erst zerstört, nachdem sich das Objekt schon lange nicht mehr im Gültigkeitsbereich befindet.

Zur Ergänzung der Garbage Collection können die Klassen einen Mechanismus bereitstellen, um Systemressourcen aktiv zu verwalten, wenn damit die IDisposable-Schnittstelle implementiert wird. IDisposable verfügt über eine Methode (Dispose), die von Clients aufgerufen werden sollte, wenn ein Objekt nicht mehr verwendet wird. Sie können die Dispose-Methode verwenden, um Ressourcen sofort freizugeben und Aufgaben, z. B. das Schließen von Dateien und das Beenden von Datenbankverbindungen, auszuführen. Im Unterschied zum Finalize-Destruktor wird die Dispose-Methode nicht automatisch aufgerufen. Clients einer Klasse müssen Dispose explizit aufrufen, wenn Ressourcen sofort freigegeben werden sollen.

Implementieren von IDisposable

Eine Klasse, die die IDisposable-Schnittstelle implementiert, sollte diese Codeabschnitte enthalten:

  • Ein Feld, um nachzuverfolgen, ob das Objekt freigegeben wurde:

    Protected disposed As Boolean = False
    
  • Eine Überladung von Dispose, die die Ressourcen der Klasse freigibt. Diese Methode sollte von der Dispose-Methode und der Finalize-Methode der Basisklasse aufgerufen werden:

    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
    
  • Eine Implementierung von Dispose, die nur folgenden Code enthält:

    Public Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub
    
  • Eine Überschreibung der Finalize-Methode, die nur folgenden Code enthält:

    Protected Overrides Sub Finalize()
        Dispose(False)
        MyBase.Finalize()
    End Sub
    

Ableiten von einer Klasse, die IDisposable implementiert

Eine Klasse, die von einer Basisklasse abgeleitet wird, die die IDisposable-Schnittstelle implementiert, muss keine Basismethoden überschreiben, es sei denn, sie verwendet zusätzliche Ressourcen, die freigegeben werden müssen. In diesem Fall muss die abgeleitete Klasse die Dispose(disposing)-Methode der Basisklasse überschreiben, um die Ressourcen der abgeleiteten Klasse freizugeben. Diese Überschreibung muss die Dispose(disposing)-Methode der Basisklasse aufrufen.

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

Eine abgeleitete Klasse darf die Dispose-Methode und die Finalize-Methode der Basisklasse nicht überschreiben. Wenn diese Methoden aus einer Instanz der abgeleiteten Klasse aufgerufen werden, ruft die Basisklassenimplementierung dieser Methoden die Überschreibung der Dispose(disposing)-Methode der abgeleiteten Klasse auf.

Garbage Collection und Finalize-Destruktor

.NET Framework verwendet das System der verweisverfolgenden Garbage Collection, um nicht verwendete Ressourcen in regelmäßigen Abständen freizugeben. In Visual Basic 6.0 und früheren Versionen wurde ein anderes System mit der Bezeichnung Verweiszählung zum Verwalten von Ressourcen verwendet. Obwohl beide Systeme dieselbe Funktion automatisch ausführen, gibt es doch einige bedeutende Unterschiede.

Die CLR zerstört Objekte in regelmäßigen Abständen, wenn das System feststellt, dass diese Objekte nicht mehr benötigt werden. Wenn es in einem System zu einem Ressourcenengpass kommt, werden Objekte schneller freigegeben, wenn reichlich Ressourcen vorhanden sind, werden Objekte dagegen weniger schnell freigegeben. Die Verzögerung zwischen dem Verlust des Gültigkeitsbereichs eines Objekts und der Freigabe durch die CLR bedeutet, dass Sie im Unterschied zu Visual Basic 6.0 und früheren Versionen nicht genau bestimmen können, wann ein Objekt zerstört wird. Daher weisen Objekte eine nicht deterministische Lebensdauer auf. In den meisten Fällen hat eine nicht deterministische Lebensdauer keinen Einfluss auf das Schreiben von Anwendungen, solange beachtet wird, dass der Finalize-Destruktor möglicherweise nicht sofort nach Verlust des Gültigkeitsbereichs eines Objekts ausgeführt wird.

Ein weiterer Unterschied zu Garbage Collection-Systemen betrifft die Verwendung von Nothing. Um vom Vorteil der Verweiszählung in Visual Basic 6.0 und früheren Versionen Gebrauch zu machen, wiesen Programmierer zuweilen Objektvariablen Nothing zu, um die an diese Variablen gebundenen Verweise freizugeben. Wenn die Variable den letzten Verweis auf das Objekt enthielt, wurden die Ressourcen des Objekts sofort freigegeben. Zwar gibt es in höheren Visual Basic-Versionen immer noch Fälle, in denen diese Prozedur sinnvoll ist, doch führt deren Ausführung nie dazu, dass das Objekt, auf das verwiesen wird, seine Ressourcen sofort freigibt. Um Ressourcen sofort freizugeben, verwenden Sie ggf. die Dispose-Methode des Objekts. Sie sollten eine Variable nur dann auf Nothing festlegen, wenn sie im Vergleich zu dem Zeitraum, den der Garbage Collector zum Auffinden verwaister Objekte benötigt, über eine lange Lebensdauer verfügt.

Siehe auch

Referenz

Operator New (Visual Basic)

Dispose

Nothing (Visual Basic)

Konzepte

Initialisierung und Beendigung von Komponenten

Finalize-Methoden und Destruktoren