Visual Basic .NET: Parameter

Aus Wikibooks

Hier noch einmal der Code des Beispiels aus dem vorherigen Kapitel:

Code:  

Module Beispiel
    Dim a As Integer
    Sub Main()
        System.Console.Write("Geben Sie eine Zahl ein: ")
        a = Console.ReadLine()
        Do
            a = 2 * a
            If a = 4 Then
                beendeDasProgramm()
            End If
        Loop Until a > 100
        beendeDasProgramm()
    End Sub
    Sub beendeDasProgramm()
        Console.WriteLine(a)
        Console.ReadLine()
        End
    End Sub
End Module

Ausgabe:  

4 (für a = 1)
4 (für a = 2)
192 (für a = 3)
128 (für a = 4)

Die Funktion BeendeDasProgramm erledigt eine ganz spezifische Aufgabe, nämlich die Ausgabe der Variable a, gefolgt von der Beendigung des Programms. Manchmal aber sollen Funktionen flexibel sein und auf bestimmte Situationen reagieren. Sie denken jetzt vielleicht an Bedingungen, doch ich meine etwas anderes. Denken Sie zum Beispiel an die Funktion MessageBox.Show. Das Meldungsfeld soll nicht immer den gleichen Text ausgeben, sondern jedesmal einen anderen Inhalt haben. Sie wissen im Prinzip auch schon die Lösung, nämlich, dass man den gewünschten Text im Klammernpaar hinter dem Funktionsbezeichner MessageBox.Show eintragen muss. Dieser Text heißt Parameter.

Eine Funktion kann aber nur Parameter annehmen, auf die Sie durch eine entsprechende Funktionsdeklaration und -definition vorbereitet ist. Wenn Sie im obigen Beispiel statt BeendeDasProgramm() den Befehl BeendeDasProgramm(5) eintragen, wird Ihnen Ihre Entwicklungsumgebung einen Fehler melden. Die Funktion weiß nämlich nichts mit dem Parameter anzufangen. Soll vielleicht 5 ausgegeben werden, oder der Wert von a fünfmal ausgegeben werden?

Es ist aber sehr einfach möglich, Parameter in die Funktionsdeklaration einzuschließen. Dazu benötigen wir aber einiges Vorwissen über das Wesen von Parametern. Im Funktionsaufruf sind Parameter wie Werte. Sie können als Literal angegeben werden, also z.B. als direkter Zahlenwert oder direkte Zeichenkette. Dieser Wert kann aber auch ein Ausdruck sein, also eine Kombination aus literalen Werten, Operatoren, Variablennamen und Klammern.

In der Funktion verhalten sich Parameter wie Variablen. Die Variablen werden beim Funktionsaufruf erstellt und mit den im Befehlsaufruf angegebenen Werten initialisiert. Man sagt, die Werte werden an die Funktion übergeben.

Das hört sich jetzt alles sehr theoretisch an. Deshalb möchte ich die ganze Sache jetzt mit einem Beispiel veranschaulichen. Dazu soll das obige Programm erweitert werden. Die Funktion BeendeDasProgramm soll einen Parameter erhalten, der den Endwert enthält, also den Wert, der ausgegeben werden soll. Dazu müssen wir zuerst die Funktionsdeklaration erweitern:

Code:  

Private Sub BeendeDasProgramm()

Der Parameter wird im Klammernpaar notiert. Die Schreibweise ähnelt der Dim-Anweisung, nur ohne Dim.

Code:  

Private Sub BeendeDasProgramm(Endwert As Integer)

In der Klammer steht der Parametername, gefolgt von As und dem Datentyp. Alles wie gehabt. Der Parametername ist außerhalb der Funktion unerheblich und dient hier nur als Hilfe, damit man, wenn man den Funktionsaufruf aufschreibt, noch weiß, was der Parameter bedeutet. Innerhalb der Funktion verhält sich der Parameter dann wie eine Variable. Der Parametername wird zum Variablennamen. Man kann die Parametervariable wie eine normale Variable verwenden, auch Zuweisungen sind möglich.

Code:  

Private Sub BeendeDasProgramm(Endwert As Integer)
    MessageBox.Show(Endwert)
    End
End Sub

So sieht dann die ganze Funktion aus. Anstatt der globalen Variable a wird nun der Parameter Endwert verwendet. Dies hat in der Praxis den Vorteil, dass die Funktion auch in einem anderen Kontext wiederverwendet werden kann. Sie erfordert jetzt nämlich nichts mehr, was sie nicht selbst deklariert. Hätte man die Funktion in der Fassung ohne Parameter in ein anderes Programm übernommen, wäre wahrscheinlich ein Fehler aufgetreten, da die Variable a nicht mit übernommen wurde oder, schlimmer noch, eine andere Variable a würde zweckentfremdet.

Natürlich erfordert das geänderte Verhalten der Funktion BeendeDasProgramm auch andere Funktionsaufrufe. So sieht der komplette Code des Programms aus.

Code:  

Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim a As Integer = InputBox("Bitte Zahl eingeben:")
        Do
            a = 2 * a
            If a = 4 Then BeendeDasProgramm(a)
        Loop Until a > 100
        BeendeDasProgramm(a)
    End Sub

    Private Sub BeendeDasProgramm(Endwert As Integer)
        MessageBox.Show(Endwert)
        End
    End Sub

End Class

Ausgabe:  

4 (für a = 1)
4 (für a = 2)
192 (für a = 3)
128 (für a = 4)

Wie Ihnen sicher auffällt, ist a jetzt wieder eine lokale Variable von Form1_Load. Man sollte Variablen immer lokal und nur in notwendigen Fällen global definieren. Dies erfolgt aus bestimmten Aspekten heraus: Erstens ist (wie oben schon erläutert) die Funktion selbstständig und kann auch in andere Projekte übernommen werden, ohne dass Schwierigkeiten bei der Portierung auftreten. Zweitens bannt eine lokale Variable die Gefahr, dass eine andere Funktion auf etwas zugreift, auf dass sie nicht zugreifen soll. (Das ist Teil des Kapselungsprinzips, eines grundlegenden Merkmals der OOP.)

Eine Funktion kann natürlich mehrere Parameter enthalten. Die einzelnen Parameterdeklarationen werden durch Kommata separiert. Ein Beispiel liefert die Funktionsdeklaration von Form1_Load, die zwei Parameter deklariert.

ByVal und ByRef[Bearbeiten]

Grafik zu Variablen, ByVal und ByRef

Vielleicht hat ihre Entwicklungsumgebung beim Eintippen der Funktionsdeklaration von BeendeDasProgramm vor Endwert das Schlüsselwort ByVal hinzugefügt. ByVal ist bei Parameterdeklarationen optional. Manche IDE fügt es automatisch an, da ein Fehlen eines entsprechenden Schlüsselwortes dazu führt, dass der Standard ByVal angenommen wird. Das automatische Einfügen dient der Übersichtlichkeit, da man dann immer weiß, was für einen Parameter man hat. Neben ByVal gibt es auch das ByRef-Verhalten. Diese Schlüsselwörter legen fest, wie aus dem übergebenen Wert ein Parameter erstellt wird.

Um zu verstehen, was ByVal und ByRef bedeuten, muss man wissen, was Variablen sind. Variablen sind Zeiger. Sie enthalten die Adresse einer bestimmten Speicherzelle. Sie zeigen auf diese Zelle. Die Zelle wiederum enthält den Wert, der in der Variablen gespeichert ist. Im Bild rechts ist dies in der zweiten Zeile veranschaulicht.

Ist ein Parameter als ByVal gekennzeichnet, bedeutet dies, dass vom übergebenen Wert eine exakte Kopie im Speicher angelegt wird. Die Parametervariable zeigt auf diese Kopie. Wenn man innerhalb der Funktion die Parametervariable mit einem neuen Wert belegt, wirkt sich das nur auf die Kopie aus. Der eigentlich übergebene Wert wird nicht geändert.

Anders bei ByRef: Eine solche Parametervariable zeigt auf den ursprünglich übergebenen Wert. Wird in der Funktion der Wert der Parametervariable geändert, wirkt sich diese Änderung unmittelbar auch auf den eigentlich übergebenen Wert in der aufgerufenen Funktion aus.

Was muss man davon jetzt wissen? Nur soviel: ByVal-Parameter kann man beliebig ändern, ohne dass die aufrufende Funktion etwas davon merkt. Änderungen an ByRef-Parametern übertragen sich jedoch auf den aufrufenden Wert. Ein Beispiel soll das verdeutlichen.

Code:  

Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim a As Integer = 5
        Funktion_Mit_ByVal(a)
        MessageBox.Show("In Form1_Load nach ByVal: " & a)
        Funktion_Mit_ByRef(a)
        MessageBox.Show("In Form1_Load nach ByRef: " & a)
    End Sub

    Private Sub Funktion_Mit_ByVal(ByVal Wert As Integer)
        MessageBox.Show("In ByVal vor Erhöhung: " & Wert)
        Wert += 1
        MessageBox.Show("In ByVal nach Erhöhung: " & Wert)
    End Sub

    Private Sub Funktion_Mit_ByRef(ByRef Wert As Integer)
        MessageBox.Show("In ByRef vor Erhöhung: " & Wert)
        Wert += 1
        MessageBox.Show("In ByRef nach Erhöhung: " & Wert)
    End Sub

End Class

Ausgabe:  

In ByVal vor Erhöhung: 5
In ByVal nach Erhöhung: 6
In Form1_Load nach ByVal: 5
In ByRef vor Erhöhung: 5
In ByRef nach Erhöhung: 6
In Form1_Load nach ByRef: 6

Was ist passiert? Nachdem a mit 5 belegt wurde, wird die ByVal-Funktion aufgerufen. Dabei wird eine lokale Kopie des Wertes von a erstellt. Der Wert dieser Kopie wird dann um 1 erhöht. (Meldungen 1 und 2) Auf die Variable a wirkt sich die Erhöhung nicht aus, da die Erhöhung an einer Kopie vorgenommen wurde. a ist gleich geblieben (Meldung 3). Nun wird die ByRef-Funktion aufgerufen. Die Parametervariable verwendet keine Kopie des Wertes von a, sondern den Wert von a selber. Deshalb wirkt sich die Erhöhung in der ByRef-Funktion (Meldungen 4 und 5) auch auf die Variable a aus, denn sowohl a als auch die ByRef-Parametervariable benutzen dieselbe Speicherzelle.

Felder als Parameter[Bearbeiten]

Manchmal (meist sind diese Fälle sehr selten) möchte man auch ein Feld als Parameter übergeben. Das ist möglich, unter einer Einschränkung: Sie dürfen keine Feldgrenzen angeben. Außerdem verschiebt sich das Klammerpaar, das bei Felddeklarationen für gewöhnlich hinter dem Feldnamen steht, hinter den Datentyp. Das folgende Beispiel zeigt zwei mögliche Funktionsdeklarationen. Beachten Sie, dass innerhalb der aufgerufenen Funktion die Größe des Feldes zunächst unbekannt ist. Mithilfe der GetLowerBound- und GetUpperBound-Funktion kann die Feldgröße jedoch bestimmt werden.

Code:  

Private Sub Feldfunktion(ByVal EindimensionalesFeld As Integer(), ByVal ZusatzInformation As Integer)
Private Sub Mehrdimensional(ByVal DreidimensionalesFeld As Integer(,,))