Visual Basic .NET: Funktionen
Aus Wikibooks
Oft gibt es in einem Programm wiederkehrende Aufgaben, für die man denselben Code verwenden kann. Stellen Sie sich einen Webbrowser vor, der immer wieder Dateien aus dem Internet herunterladen muss, seien es Webseiten, Bilder oder Archive. Intutitiv würde man den entsprechenden Code immer wieder verwenden. Nun stellen Sie sich vor, Sie haben im Download-Code einen Fehler gefunden. Nun müssten Sie jedes Vorkommnis des Fehlers suchen und ausmerzen, was anstrengend und langweilig ist und eine hohe Gefahr von Fehlern mit sich bringt, etwa dass Sie ein Vorkommen übersehen oder sich vertippen.
Zum Glück bietet Visual Basic eine Möglichkeit, wiederkehrende Aufgaben nur einmal zu definieren und eine Schnittstelle bereitzustellen, die unendlich oft wiederverwendet werden kann, um diese Aufgabe anhand des vorhandenen Codes zu lösen. Diese Möglichkeit heißt Funktion. Schauen Sie sich das folgende Beispiel an. Ihnen wird auffallen, dass jetzt wieder der komplette Code notiert wird, da wir nicht mehr nur Form1_Load bearbeiten.
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 End Loop Until a > 100 End End Sub End Class
Probieren Sie einmal verschiedene Werte wie 5 oder 6 aus. (In solchen Fällen ist es oft eine gute Idee, sich vorher zu überlegen, was bei welchen Werten von a passieren müsste.) Beachten Sie, dass in diesem Fall keine Gültigkeitsprüfung für den eingegebenen Wert implementiert werde. Das heißt, dass nicht geprüft wird, ob überhaupt eine positive Zahl eingegeben wurde.
In diesem Code wird zweimal die End-Anweisung verwendet. Das ist soweit nicht schlimm. Stellen Sie sich nun vor, Sie wollen den Wert der Variablen a ausgeben, sobald man versucht, das Programm zu schließen. Sie müssten nun alle Vorkommnisse der End-Anweisung um einen solchen Befehl erweitern.
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 MessageBox.Show(a) End End If Loop Until a > 100 MessageBox.Show(a) End End Sub End Class
4 (für a = 1) 4 (für a = 2) 192 (für a = 3) 128 (für a = 4)
Auf den ersten Blick lassen sich der Code und insbesondere die MessageBox.Show-Aufrufe nicht mehr so leicht überblicken, die ganze Sache wird unübersichtlich. Diese Unübersichtlichkeit verstärkt sich noch, wenn Sie mehrere solche Befehle hinzufügen, sodass das Beenden letzlich viele Zeilen umfasst. Nun werfen Sie einen Blick auf den folgenden Code, der genau das gleiche bewirkt:
Public Class Form1 Dim a As Integer Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load a = InputBox("Bitte Zahl eingeben:") Do a = 2 * a If a = 4 Then BeendeDasProgramm() Loop Until a > 100 BeendeDasProgramm() End Sub Private Sub BeendeDasProgramm() MessageBox.Show(a) End End Sub End Class
4 (für a = 1) 4 (für a = 2) 192 (für a = 3) 128 (für a = 4)
Nun wurde eine Funktion namens „BeendeDasProgramm“ definiert, ein wörtlich zu nehmender Befehl an den Computer, das Programm zu beenden. Zwischen Private Sub BeendeDasProgramm() und End Sub finden sich die Anweisungen, die das Programm korrekt beenden. Diese Anweisungen sind aus der Form1_Load-Funktion verschwunden, stattdessen sehen wir BeendeDasProgramm(). Dieser Befehl ruft die Funktion auf, d.h. es werden die Befehle und Anweisungen in der Funktion ausgeführt, dann setzt das Programm am Ursprungsort des Aufrufs fort.
Das ist das ganze Geheimnis um Funktionen, eines der mächtigsten Werkzeuge in Visual Basic. Die komplette Sprache baut auf Funktionen auf. Wir haben bereits gesehen, dass auch Form1_Load eine Funktion ist, die jedoch, wie die Zeile Private Sub Form1_Load ... zeigt, ungleich komplizierter ist. Dies hängt mit der Verknüpfung mit dem Windows-Fenstersystem zusammen, das etwa aufgrund des Handles-Abschnittes entscheidet, wann eine Funktion aufgerufen wird.
Vielleicht ist Ihnen schon aufgefallen, dass die Deklaration der Variable hier nicht innerhalb der Funktion Form1_Load passiert, sondern schon davor. Das hängt damit zusammen, dass die Variable a, wenn Sie in der Funktion Form1_Load deklariert wird, auch nur da verfügbar ist. Sie werden dann innerhalb einer anderen Funktion, hier BeendeDasProgramm, nicht auf die Variable zugreifen können. In diesem Fall ist die Variable lokal, denn sie ist auf eine bestimmte Lokalität, hier die Funktion Form1_Load beschränkt. Wenn eine Variable in mehreren Funktionen verfügbar sein soll, muss sie global deklariert werden. Das bedeutet, dass die Variable nicht an eine bestimmte Funktion gebunden ist, sondern in allen Funktionen verfügbar ist. Um eine Variable global zu deklarieren, notieren Sie die Dim-Anweisung zwischen Public Class Form1 und End Class, aber nicht innerhalb einer Funktion. Der Übersicht halber werden die Dim-Anweisungen für globale Variablen meist ganz oben in die Klassendefinition, also direkt nach Public Class ..., gestellt. Sie können die Deklarationen aber auch zwischen zwei Funktionen platzieren. Wir werden uns später noch einmal genauer und allgemeiner mit globalen und lokalen Variablen befassen.
[Bearbeiten] Arbeit mit Funktionen
Das Beispiel zeigt, wie Funktionen definiert werden: Die erste Zeile der Funktion beginnt mit Private Sub, gefolgt vom Funktionsnamen und einem leeren Klammerpaar. (Wir werden dieses Paar bald mit Leben füllen.) Für Funktionsnamen gelten die gleichen Regeln wie für Variablennamen: Erlaubt sind nur Buchstaben, Ziffern und Unterstriche, das erste Zeichen muss ein Buchstabe sein. Diese einleitende Zeile heißt Funktionskopf. Sie charakterisiert die Schnittstelle, über die die Funktion verwendet wird. Da dieses Verhalten ähnlich zur Variablendeklaration ist, die die Variable charakterisiert (über den Datentyp und evtl. die Feldgrenzen), heißt der Funktionskopf auch Funktionsdeklaration.
Nach dieser einleitenden Zeile folgt der sogenannte Funktionsrumpf. Er enthält alle Anweisungen und Befehle, die von der Funktion ausgeführt werden, sobald sie aufgerufen wird. Abschließend begrenzt die Anweisung End Sub die Funktion nach unten hin. Da dieser Bereich das Verhalten der Funktion definiert, heißt der Funktionsrumpf auch Funktionsdefinition.
Der Inhalt von Form1_Load zeigt uns, wie ein Funktionsaufruf auszusehen hat. Er besteht aus einem Namen, mit dem die Funktion eindeutig identifiziert wird (ich sage bewusst nicht einfach „dem Funktionsnamen“; in diesem Fall ist es aber einfach nur BeendeDasProgramm) und einem leeren Klammerpaar (welches wir ebenfalls bald mit Leben füllen werden).
[Bearbeiten] Ordnung
Alle Funktionsaufrufe sind Befehle. Auch der Befehl System.Windows.Forms.MessageBox.Show ... ist ein Funktionsaufruf. Als aufmerksamer Leser sollte sich Ihnen jetzt auffallen, dass Funktionsnamen keine Punkte enthalten dürfen, dieser es aber offensichtlicherweise tut. Das ist richtig und falsch. Die Funktion, die wir hier aufrufen, heißt nicht System.Windows.Forms.MessageBox.Show, sondern nur Show. Ich habe ja eben bewusst gesagt, dass der Funktionsaufruf einen Namen enthält, „mit dem die Funktion eindeutig identifiziert wird“. Dieser Name heißt auch Funktionsbezeichner.
Denken Sie zur Erklärung an eines der ersten Kapitel, in dem es um das „Hello World“-Programm ging. .NET stellt uns tausende von Objekten mit zahlreichen Funktionen (im wahrsten Sinne des Wortes) zur Verfügung. Die Objekte sind in Namensräumen geordnet, die wiederum in Namensräumen geordnet sein können. Ähnlich ist es mit Dateien (Objekten), die in Ordnern (Namensräumen) geordnet werden, die wiederum in Ordnern geordnet werden können.
Dieser Ordnung gerecht werdend gehört auch die Funktion BeendeDasProgramm zum Objekt Form1. Vielleicht denken Sie jetzt, dass der Aufruf über den Bezeichner Form1.BeendeDasProgramm erfolgen müsste. Wenn Sie allein zu dieser Überlegung gelangt sind, haben Sie schon eines der wesentlichen Konzepte der Objektorientierten Programmierung (OOP) verstanden, dass jedoch erst viel später genauer ausgeführt wird. Dort werden Sie auch sehen, dass Form1.BeendeDasProgramm falsch ist; es müsste Me.BeendeDasProgramm heißen. Doch dafür muss man sich genauer mit OOP beschäftigen, was an dieser Stelle noch nicht das Thema sein soll. Für jetzt nur soviel: Da Form1_Load zum selben Objekt wie BeendeDasProgramm gehört, nämlich zu Form1, reicht es, wenn man einfach nur den Funktionsnamen als Bezeichner verwendet, also die Funktion nur über den Bezeichner BeendeDasProgramm aufruft. Das wird bis zur Einführung in die OOP immer so sein, denn erst dann werden wir mehrere Objekte verwenden.

