Visual Basic .NET: Blöcke

Quellcode in Visual Basic .NET besteht aus einzelnen Blöcken, bestimmten Befehlsgruppen. Blöcke werden durch Anweisungen definiert. Zum Beispiel definiert eine If-Anweisung zwei Blöcke: einen zwischen If ... Then und Else, dessen Anweisungen ausgeführt werden, wenn die Bedingung True ist, und einen zwischen Else und End If, dessen Anweisungen ausgeführt werden, wenn die Bedingung zu False aufgelöst wird. Blöcke können verschachtelt (kaskadiert) werden, d.h. Blöcke können andere Blöcke enthalten. Zum Beispiel kann in einem Block einer If-Anweisung wieder eine If-Anweisung stehen.

Nur manche Anweisungen bilden Blöcke. Ein zweites Beispiel für eine blockbildende Anweisung sind die Sub- und Function-Anweisungen, welche Prozeduren und Funktionen deklarieren und definieren. (Wie Sie sehen, werden Anweisungen immer nach dem Schlüsselwort benannt, das sie kennzeichnet.) Diese Anweisungen bilden nur einen Block. Die Anweisungen dieses Blockes werden ausgeführt, wenn die Prozedur oder Funktion ausgeführt wird.

Lebenszyklus von Variablen

Bearbeiten

Vor einiger Zeit hatten wir die Begriffe „lokale Variable“ und „globale Variable“ eingeführt. Lokale Variablen werden innerhalb einer Funktion deklariert und sind deshalb nur dort verfügbar. Globale Variablen werden außerhalb einer bestimmten Funktion deklariert und sind in mehreren Funktionen gültig. Die Begriffe der lokalen und globalen Variablen werden wir nun zum Begriff des Variablenlebenszyklus erweitern. Dieser Begriff lässt sich so zusammenfassen: Eine Variable ist nur in dem Block verfügbar, in dem sie deklariert wurde.

Bis jetzt haben wir uns immer nur mit dem Zeitpunkt befasst, von dem an die Variable existiert, also der Deklaration. Wichtig ist aber für den Lebenszyklus auch, wann die Variable aufhört, zu existieren. Da dies noch ein sehr junger Aspekt ist, gibt es noch keinen anerkannten deutschen Namen dafür. Das .NET-Framework verwendet den engl. Begriff „finalization“ (engl. finalize = beenden, vollenden). Aufgrund des obenstehenden Satzes zur Variablensichtbarkeit wird eine Variable finalisiert, wenn der Block, in dem die Variable deklariert wurde, beendet wird, also wenn die Ausführung dieses Blockes abgeschlossen wurde. (Dieser Block wird auch Kontext der Variable genannt.)

 
Achtung: Im Codebeispiel rechts fehlt die sonst bei uns übliche Form1_Load-Funktion. Die farbigen Markierungen links zeigen die Lebensdauer der Variable an, also den Bereich des Codes, in dem die Variable verfügbar ist.

Die Grafik rechts verdeutlicht den Lebenszyklus verschiedener Variablen.

Die Class-Anweisung (die wir später kennenlernen werden) definiert einen Block, in die Funktionen eines Objektes (hier Form1) zusammengefasst werden. Variable a ist in diesem Block notiert und gilt deshalb während der Lebenszeit des Objektes Form1. Das heißt, dass Sie die Variable a verwenden können, wenn das Objekt Form1 erstellt wurde. (Das geschieht hier automatisch mit dem Start des Programmes.) Innerhalb der Funktionen von Form1 können Sie a garantiert immer verwenden, da diese Funktionen nur aufgerufen werden können, wenn Form1 erstellt wurde.

Die Variable b ist im Block der Sub-Anweisung deklariert. Sie gilt nur innerhalb der durch die Sub-Anweisung deklarierten und definierten Prozedur. Außerhalb dieser Prozedur können Sie b nicht verwenden. Beachten Sie, dass a beim Erstellen des Objektes Form1 automatisch erstellt wird, egal wo die Deklaration von a im Class-Block steht. b hingegen ist vor der Deklaration auch innerhalb des Blockes nicht verfügbar, sondern erst, wenn die Dim-Anweisung ausgeführt wurde.

Die Variable c stellt eine Besonderheit innerhalb dieser Thematik dar. Rein theoretisch wurde Sie im Sub-Block definiert, da die For-Anweisung zu selbigem gehört. Allerdings wird die Variable nicht erst am Ende des Sub-Blockes finalisiert, sondern bereits am Ende der For-Anweisung. Das ist die berühmte Ausnahme von der Regel.

Mit der Variable d muss man vorsichtig sein. Da der For-Block mit jedem Schleifendurchlauf neu ausgeführt wird, wird d auch jedesmal neu deklariert. Die Werte eines Schleifendurchlaufes stehen im nächsten Schleifendurchlauf also nicht mehr zur Verfügung. Sie müssen die Ergebnisse eines Schleifendurchlaufes also ggf. in einem höheren Kontext (hier in einer Variable im Sub-Block) zwischenspeichern.

Variablensichtbarkeit

Bearbeiten

Im Zusammenhang mit dem Lebenszyklus von Variablen tritt häufig der Begriff Variablensichtbarkeit auf. Man sagt, eine Variable ist in einem bestimmten Bereich des Codes sichtbar, wenn man aus diesem Abschnitt heraus auf die Variable zugreifen kann. Oben ist die Variable a in allen Funktionen des Objektes Form1 sichtbar. Die Variablen b, c und d wären in einer zweiten Funktion des Objektes Form1 nicht sichtbar.

Die Variablensichtbarkeit ist eine oft unterschätzte Fehlerquelle, so wie im folgenden Beispiel.

  Code:  

Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim Eingabe As String = InputBox("Geben Sie bitte ""Hallo"" ein.")
        If Eingabe = "Hallo" Then
            Dim Ausgabe As String = "Gut."
        Else
            Dim Ausgabe As String = "Falsch."
        End If
        MessageBox.Show(Ausgabe)
    End Sub
End Class

  Ausgabe:  

Fehler

Ein Fehler, wie er im Lehrbuch stehen könnte (und es hier ja auch tut). Die Variable(n) Ausgabe wird/werden im Then- und im Else-Block deklariert. Am Ende der Blöcke werden sie finalisiert. Beim Aufruf der MessageBox.Show-Funktion sind sie deshalb nicht mehr sichtbar; der Zugriff auf die dort unbekannte Variable Ausgabe versucht einen Fehler. Sie können den Fehler umgehen, indem Sie die Variable(n) Ausgabe in den nächsthöheren Kontext verschieben.

  Code:  

Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim Eingabe As String = InputBox("Geben Sie bitte ""Hallo"" ein.")
        Dim Ausgabe As String
        If Eingabe = "Hallo" Then
            Ausgabe = "Gut."
        Else
            Ausgabe = "Falsch."
        End If
        MessageBox.Show(Ausgabe)
    End Sub
End Class

  Ausgabe:  

Gut. (Falls "Hallo" eingegeben wurde.)
Falsch. (Andernfalls.)

Shadowing

Bearbeiten

Dass Variablen an den deklarierenden Block gebunden sind, bringt noch ein anderes Problem mit sich. Der folgende Code zeigt, was ich meine.

  Code:  

Dim Eingabe As String = InputBox("Sag hallo.")
Dim Ausgabe As String = "Gut."
If Eingabe = "Hallo" Then
    Dim Ausgabe As String = "Richtig."
Else
    Dim Ausgabe As String = "Falsch."
End If
MessageBox.Show(Ausgabe)

  Ausgabe:  

Gut.

Erstaunlicherweise kann man dieses Beispiel kompilieren und ausführen. Allerdings ist das Ergebnis nicht das erwartete: Statt „Richtig.“ oder „Falsch.“ wird „Gut.“ angezeigt. Den Effekt, der das bewirkt, nennt man Shadowing (engl. shadow = Schatten). Das bedeutet folgendes: Ein Block deklariert eine Variable mit einem bestimmten Namen, hier Ausgabe. Ein zweiter Block, der sich in diesem Block befindet, deklariert eine Variable desselben Namens. In dem zweiten Block ist nun unter dem Namen die eigene Variable sichtbar, die Variable des höheren Kontexts ist dort nicht mehr sichtbar. Sie steht quasi im Schatten der zweiten Variable, daher der Name.

Für unser Beispiel bedeutet das folgendes: Der oberste Block (also höchstwahrscheinlich der Sub-Block der Prozedur Form1_Load) definiert in der zweiten Zeile eine Variable Ausgabe, die mit dem Wert „Gut.“ initialisiert wird. Der Then-Block und der Else-Block definieren jeweils eigene Variablen mit dem Namen Ausgabe. Diese führen im jeweiligen Block Shadowing auf die Ausgabevariable des Sub-Blockes durch, sodass diese dort nicht mehr sichtbar ist. Mit Ende des Then- bzw. des Else-Blockes werden diese Variablen finalisiert. Durch die Zuweisungen im Then- und im Else-Block hat sich an der Ausgabevariable des Sub-Blockes nichts geändert, da diese dort nicht sichtbar war. Deshalb wird der ursprüngliche Wert, nämlich „Gut.“, ausgegeben.