Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
1 Einführung
2 Grundlagen der Sprachsyntax
3 Klassendesign
4 Weitere Datentypen
5 Multithreading
6 Collections und LINQ
7 Eingabe und Ausgabe
8 Anwendungen: Struktur und Installation
9 Code erstellen und debuggen
10 Einige Basisklassen
11 Windows-Anwendungen erstellen
12 Die wichtigsten Steuerelemente
13 Tastatur- und Mausereignisse
14 MDI-Anwendungen
15 Grafiken mit GDI+
16 Drucken
17 Entwickeln von Steuerelementen
18 Programmiertechniken
19 WPF – Grundlagen
20 Layoutcontainer
21 WPF-Steuerelemente
22 Konzepte von WPF
23 Datenbankverbindung mit ADO.NET
24 Datenbankabfragen mit ADO.NET
25 DataAdapter
26 Offline mit DataSet
27 Datenbanken aktualisieren
28 Stark typisierte DataSets
A Anhang: Einige Übersichten
Stichwort

Jetzt Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Visual Basic 2008 von Andreas Kuehnel, Stephan Leibbrandt
Das umfassende Handbuch
Buch: Visual Basic 2008

Visual Basic 2008
3., aktualisierte und erweiterte Auflage, geb., mit DVD
1.323 S., 49,90 Euro
Rheinwerk Computing
ISBN 978-3-8362-1171-0
Pfeil 3 Klassendesign
Pfeil 3.1 Objektorientierung
Pfeil 3.1.1 Einführung
Pfeil 3.1.2 Vorteile
Pfeil 3.1.3 Klassenimplementierung in .NET
Pfeil 3.1.4 Klassen in Visual Basic
Pfeil 3.1.5 Projekttyp »Klassenbibliothek«
Pfeil 3.1.6 Bemerkung zu den Codefragmenten
Pfeil 3.1.7 Objekte durch New
Pfeil 3.1.8 Ausnahmen mit Throw auslösen
Pfeil 3.1.9 Datentypen
Pfeil 3.1.10 Sichtbarkeit der Klasse
Pfeil 3.1.11 Aufteilung der Definition mit Partial
Pfeil 3.1.12 Grafikbibliothek: Beispiel für Kapitel 3 und 4
Pfeil 3.2 Kapselung
Pfeil 3.2.1 Kombinationen
Pfeil 3.2.2 Private
Pfeil 3.2.3 Sichtbarkeitsmodifikatoren
Pfeil 3.2.4 Lokale Variablen
Pfeil 3.2.5 Softwareschutz
Pfeil 3.2.6 Grafikbibliothek: private Größe des Rechtecks
Pfeil 3.3 Verhalten (Methoden)
Pfeil 3.3.1 Prinzip
Pfeil 3.3.2 Verlassen der Methode
Pfeil 3.3.3 Parameter
Pfeil 3.3.4 Überladung (Overloads)
Pfeil 3.3.5 Rückgabewert
Pfeil 3.3.6 Reine Deklaration mit Partial
Pfeil 3.3.7 Grafikbibliothek: Zugriffsmethoden auf die Größe des Rechtecks
Pfeil 3.4 Bindung
Pfeil 3.4.1 Klassenbindung mit Shared
Pfeil 3.4.2 Klassenkonstruktoren
Pfeil 3.4.3 Externe Funktionen
Pfeil 3.4.4 Grafikbibliothek: Rechteckübergreifendes
Pfeil 3.5 Objektinitialisierung mit Konstruktoren
Pfeil 3.5.1 Objektkonstruktoren
Pfeil 3.5.2 Nichtöffentliche Konstrukturen
Pfeil 3.5.3 Grafikbibliothek: Initialisierung des Rechtecks
Pfeil 3.6 Zustände (Felder)
Pfeil 3.6.1 Deklaration und Initialisierung
Pfeil 3.6.2 Sichtbarkeit
Pfeil 3.6.3 Konstanten: ReadOnly und Const
Pfeil 3.6.4 With
Pfeil 3.6.5 Grafikbibliothek: Konstanten des Rechtecks
Pfeil 3.7 Eigenschaften
Pfeil 3.7.1 Kontrolle beim Aufrufer
Pfeil 3.7.2 Zugriffsmethoden
Pfeil 3.7.3 Getter und Setter: Property
Pfeil 3.7.4 Indexer
Pfeil 3.7.5 Standardeigenschaft: Default
Pfeil 3.7.6 Schreibschutz und Leseschutz: ReadOnly und WriteOnly
Pfeil 3.7.7 Sichtbarkeit
Pfeil 3.7.8 Klammern
Pfeil 3.7.9 Grafikbibliothek: Eigenschaften des Rechtecks
Pfeil 3.8 Innere Klassen
Pfeil 3.8.1 Beziehung zur äußeren Klasse
Pfeil 3.8.2 Sichtbarkeit
Pfeil 3.8.3 Grafikbibliothek: Position des Rechtecks
Pfeil 3.9 Dynamisches Verhalten: Delegate und Function
Pfeil 3.9.1 Funktionszeiger: Delegates
Pfeil 3.9.2 Automatisch generierter Code
Pfeil 3.9.3 Mehrere Aktionen gleichzeitig
Pfeil 3.9.4 Asynchrone Aufrufe
Pfeil 3.9.5 Funktionsobjekte: Function (λ-Ausdrücke)
Pfeil 3.9.6 Umwandlungen
Pfeil 3.9.7 Grafikbibliothek: Vergleich von Rechtecken
Pfeil 3.10 Ereignisse
Pfeil 3.10.1 Ereignis: Event und RaiseEvent
Pfeil 3.10.2 Statische Methodenbindung WithEvents und Handles
Pfeil 3.10.3 Dynamische Methodenbindung: AddHandler und RemoveHandler
Pfeil 3.10.4 Benutzerdefinierte Ereignisse: Custom Event
Pfeil 3.10.5 Umwandlungen
Pfeil 3.10.6 Grafikbibliothek: Größenänderungen von Rechtecken überwachen
Pfeil 3.11 Benutzerdefinierte Operatoren
Pfeil 3.11.1 Prinzip
Pfeil 3.11.2 Überladung
Pfeil 3.11.3 Vergleich
Pfeil 3.11.4 Typumwandlung mit CType: Widening und Narrowing
Pfeil 3.11.5 Wahrheitswerte: IsTrue und IsFalse
Pfeil 3.11.6 Grafikbibliothek: Addition und Umwandlung von Rechtecken
Pfeil 3.12 Alle Klassenelemente
Pfeil 3.12.1 Der Namensraum My
Pfeil 3.13 Vererbung
Pfeil 3.13.1 Klassenbeziehung durch Inherits
Pfeil 3.13.2 Sichtbarkeitsmodifikatoren
Pfeil 3.13.3 Zugriff auf Eltern mit MyBase
Pfeil 3.13.4 Modifikation: Shadows und Overloads (Overrides)
Pfeil 3.13.5 Abstrakte Klassen: MustInherit und MustOverride
Pfeil 3.13.6 Spezifische Catch-Blöcke
Pfeil 3.13.7 Eigene Ausnahmen
Pfeil 3.13.8 Arrays
Pfeil 3.13.9 Grafikbibliothek: neue Vielecke
Pfeil 3.14 Polymorphie
Pfeil 3.14.1 Virtuelle Methoden: Overridable und Overrides
Pfeil 3.14.2 Unterbrechen: Overloads, Shadows und Overrides
Pfeil 3.14.3 Unterbinden: NotInheritable, NotOverridable und MyClass
Pfeil 3.14.4 Konstruktoren
Pfeil 3.14.5 Equals
Pfeil 3.14.6 ToString
Pfeil 3.14.7 Grafikbibliothek: Objektsammlungen
Pfeil 3.15 Schnittstellen: Interface und Implements
Pfeil 3.15.1 Benennungen und Parameter
Pfeil 3.15.2 Schnittstellen und Vererbung
Pfeil 3.15.3 Schnittstellenvererbung
Pfeil 3.15.4 Schnittstelle oder abstrakte Klasse?
Pfeil 3.15.5 Grafikbibliothek: Flächen
Pfeil 3.16 Lebensende eines Objekts
Pfeil 3.16.1 Garbage Collector
Pfeil 3.16.2 Destruktoren
Pfeil 3.16.3 Dispose und Using


Rheinwerk Computing - Zum Seitenanfang

3.3 Verhalten (Methoden) Zur nächsten ÜberschriftZur vorigen Überschrift

Nach diesem Einstieg in die Objektorientierung wollen wir nun Klassen mit Leben füllen, indem wir für Objekte deren Verhalten definieren. Wenn Sie also wollen, dass ein Objekt eine bestimmte Aktion ausführen soll, schreiben Sie die entsprechenden Anweisungen in eine Methode und rufen diese auf.


Rheinwerk Computing - Zum Seitenanfang

3.3.1 Prinzip Zur nächsten ÜberschriftZur vorigen Überschrift

Jede Methode in einer Klasse folgt einer der drei folgenden Syntaxvarianten (optionale Teile stehen in eckigen Klammern, kursive Teile müssen Sie Ihren Bedürfnissen anpassen, die Typen-Spezifikation wird im Abschnitt 4.4, »Generisches«, erläutert):


[<Modifikatoren>] Sub Name[Typen] [Ort] ([<Parameter>]) [Effekt] 
  [<Anweisungen>] 
End Sub

[<Modifikatoren>] Function Name[Typen] [Ort] ([<Parameter>]) As Typ [Effekt] 
  [<Anweisungen>] 
End Function

[<Modifikatoren>] Operator op(<Parameter>) As <Typ> 
  [<Anweisungen>] 
End Operator

Die gezeigte Aufteilung in Zeilen ist zwingend, das heißt, die Zeilen mit dem Namen, den Anweisungen und das End-Konstrukt müssen jeweils in einer neuen Zeile beginnen und können nicht durch einen Doppelpunkt in weniger Zeilen zusammengefasst werden. Der Name folgt denselben Richtlinien wie bei Variablenbezeichnern (siehe Abschnitt 2.5.3, »Variablendeklaration«). Auf die Parameter gehen wir in Abschnitt 3.3.3, »Parameter«, ein. Die Anweisungen in der Funktion und dem Operator sollten den Rückgabewert festlegen (siehe Abschnitt 3.3.5, »Rückgabewert«). Die Modifikatoren und Effekte lassen sich in ein paar Gruppen einteilen (siehe Tabelle 3.3 und Tabelle 3.4).


Tabelle 3.3 Modifikatoren einer Methode

Art Beschreibung

Sichtbarkeit

Grad der Öffentlichkeit (siehe Abschnitt 3.2, »Kapselung«)

Bindung

Objekt- oder klassenbezogen (siehe Abschnitt 3.4, »Bindung«)

Redefinition

Art des Ersatzes oder Zwangs zu einer Definition (siehe Abschnitt 3.13, »Vererbung« und 3.3.4, »Überladung«)

Leer

Ohne Anweisungen (siehe Abschnitt 3.3.6, »Reine Deklaration mit Partial«)

Extern (auch Ort)

Definition in einer anderen Bibliothek (siehe Abschnitt 3.4.3, »Externe Funktionen«)

Typumwandlung

Explizit oder implizit (siehe Abschnitt 3.11.4, »Typumwandlung mit CType: Widening und Narrowing«)



Tabelle 3.4 Effekte einer Methode (schließen sich gegenseitig aus)

Art Beschreibung

Implementation

Erfüllung einer Schnittstelle (siehe Abschnitt 3.15, »Schnittstellen: Interface und Implements«)

Ereignis (nur Sub)

Verarbeitung eines Ereignisses (siehe Abschnitt 3.10, »Ereignisse«)


Es reicht an dieser Stelle, die Struktur zu verstehen. Auf die Bedeutung gehen die angegebenen Abschnitte ein.

Die Auswahl, welche Methode durch einen Aufruf angesprochen wird, ist durch die Signatur der Methode eindeutig festgelegt. Sie setzt sich zusammen aus:

  • Name: Bezeichner und gegebenenfalls Typparameter (siehe Abschnitt 4.4, »Generisches«)
  • Parameter: Anzahl und Typen unter Berücksichtigung der Reihenfolge
  • Rückgabetyp: nur bei Typumwandlungen

Folgende Dinge sind nicht Teil der Signatur:

  • Namen von Parametern
  • Modifikatoren und Effekte
  • Sub und Function
  • der Rückgabetyp (Ausnahme: Typumwandlungen)
  • Parametermodifikatoren (ByVal und ByRef)

Hinweis
Wenn für Name(Parameter) keine Definition existiert, wird nach einer Definition für Name()(Parameter) gesucht.



Rheinwerk Computing - Zum Seitenanfang

3.3.2 Verlassen der Methode Zur nächsten ÜberschriftZur vorigen Überschrift

Damit Sie im Labyrinth der Methoden nicht verloren gehen, wollen wir erst einmal schauen, wie wir eine Methode verlassen können und mit der Anweisung fortfahren, die dem Methodenaufruf folgt. Eine Methode wird beendet durch:

  • Sub: Fertigstellung, Return oder Exit Sub
  • Function: Fertigstellung, Return Wert oder Exit Function

Wenn Sie also gar nichts machen, wird die Methode auch beendet, und zwar nach der letzten Anweisung des Methodenrumpfs. Interessanter ist die explizite Steuerung des Methodenausstiegs. Das folgende Codefragment macht sowohl von Return als auch von Exit Gebrauch. Welche der Variante Sie wählen, ist Geschmackssache. Nur bei einer Funktion mit einem Rückgabewert würden sich die beiden Alternativen unterscheiden.


'...\Klassendesign\Methoden\ReturnUndExit.vb

Option Explicit On 
Namespace Klassendesign 
  Module ReturnUndExit

    Sub RechteckEingeben() 
      Dim a, b As Double 
      Console.Write("Länge: ") 
      a = Int32.Parse(Console.ReadLine()) 
      If a = 0 Then 
        Console.WriteLine("Länge 0 ergibt Fläche 0. ") 
        Console.ReadLine() 
        Return 
      End If 
      Console.Write("Breite: ") 
      b = Int32.Parse(Console.ReadLine()) 
      If b = 0 Then 
        Console.WriteLine("Breite 0 ergibt Fläche 0. ") 
        Console.ReadLine() 
        Exit Sub 
      End If 
      Console.WriteLine("Fläche {0}.", a * b) 
      Console.ReadLine() 
    End Sub

  End Module 
End Namespace

Wird eine Länge von null angegeben, wird die Methode durch das Return beendet. Bei einer Breite von null beendet Exit Sub die Methode. Ansonsten wird die Methode nach der letzten Anweisung vor End Sub verlassen. Bitte beachten Sie die ReadLine()-Anweisung vor Return und Exit Sub. Da die Methode vorzeitig beendet wird, kommt das letzte Readline() nicht zur Ausführung. Ohne ein ReadLine() wird das Fenster geschlossen, sodass das Ergebnis nicht betrachtet werden kann (siehe Abschnitt 2.3.3, »Unterbrechung mit ReadLine«).


Rheinwerk Computing - Zum Seitenanfang

3.3.3 Parameter Zur nächsten ÜberschriftZur vorigen Überschrift

Damit eine Methode nicht immer dasselbe macht, können ihr beliebig viele Parameter beliebigen Typs übergeben werden. Die Parameter

  • sind durch Kommata voneinander getrennt,
  • haben immer einen Bezeichner, auch wenn der Parameter in der Methode nicht verwendet wird,
  • haben immer einen Datentyp, der in einer As-Klausel angegeben wird, und
  • dürfen zusätzliche Modifikatoren haben.

Hinweis
Eine Methode muss keine Parameter haben.


Wie in Abschnitt 2.5.5, »Sichtbarkeit und Lebensdauer«, beschrieben wird, haben Methoden vollen Zugriff auf die Felder der sie umgebenden Klasse. Dadurch muss nicht jedes Mal die Zustandsinformation des Objekts an die Methode übergeben werden, wodurch ihr Aufruf deutlich vereinfacht wird. Außerdem darf eine Methode die Felder des Objekts ändern und so das Objekt in einen neuen Zustand versetzen.

Mit Blick auf die Verwendung der Parameter unterscheidet man zwei Arten:

  • formale Parameter: Definition im Quelltext
  • aktuelle Parameter: konkrete Werte/Variablen beim Aufruf

Hinweis
Dem formalen Datenfeldparameter name() As typ entspricht der aktuelle Datenfeldparameter ohne Klammern. Zahlenliterale werden gegebenenfalls automatisch konvertiert, wobei ein Überlauf einen Compilerfehler erzeugt.


Beim Aufruf einer Methode wird jeder formale Parameter durch den korrespondierenden aktuellen Parameter ersetzt, und dann erst werden die Anweisungen der Methode ausgeführt. Vom Standpunkt der Methode verhalten sich die formalen Parameter wie mit Dim deklarierte lokale Variablen, die mit den aktuellen Werten initialisiert wurden (kleinere Abweichungen davon werden im weiteren Verlauf erläutert).


Hinweis
Out-Parameter wie in C#, die eine Initialisierung eines Referenzparameters erzwingen, gibt es in Visual Basic nicht.


Wertübergabe mit ByVal (call by value)

Die einfachste Art, eine Methode zu steuern, besteht darin, ihr Werte zu übergeben. Bei dieser Art der Übergabe wird nur eine Kopie des Wertes an die Methode weitergegeben. Jeder Parameter hat folgende Syntax:


ByVal name As typ


Hinweis
Die Kopie ist flach. Enthält der Wert Referenzen, werden nur diese kopiert und nicht das, worauf sie zeigen.


Diese Deklaration hat den gleichen Effekt wie folgende Deklaration am Beginn der Methode:

Dim name As typ = aktuellerParameter

Im nächsten Codefragment werden einige Aspekte der Wertübergabe getestet. Jede der Prozeduren zur Ausgabe des Rechteckumfangs benutzt eine unterschiedliche Anzahl von Parametern verschiedenen Typs. Alle Parameter werden als Wert übergeben, da sie mit ByVal gekennzeichnet sind. Außerdem rufen sich die Methoden gegenseitig auf, indem sie die fehlenden Parameter ergänzen. Um das Beispiel einfacher zu halten, wurden Prozeduren (Sub) gewählt. Die Parameterübergabe an Funktionen erfolgt genauso.


'...\Klassendesign\Methoden\ByVal.vb

Option Explicit On 
Namespace Klassendesign 
  Class RechteckByVal 
    Public a, b As Double 
    Sub UmfangEinfach() 
      UmfangStellenzahl(0) 
    End Sub

    Sub UmfangStellenzahl(ByVal s As Integer) 
      UmfangEinheit("", s) 
    End Sub

    Sub UmfangEinheit(ByVal e As String, ByVal s As Integer) 
      Console.Write("Umfang {0:F" & s & "} {1} ", 2 * (a + b), e) 
    End Sub

  End Class 
  ... 
End Namespace

Das folgende Codefragment testet die Umfangsprozeduren. In UmfangNeu wird der Umfang ausgegeben und das Rechteck neu zugewiesen, während in UmfangÄndern das Rechteck verändert wird. Um den Einfluss der Zuweisungen zu kontrollieren, wird jede der Prozeduren zweimal aufgerufen:


'...\Klassendesign\Methoden\ByVal.vb

Option Explicit On 
Namespace Klassendesign 
  ... 
  Module ByValue

    Sub UmfangNeu(ByVal re As RechteckByVal) 
      re.UmfangStellenzahl(3) 
      re = New Rechteck() 
    End Sub

    Sub UmfangÄndern(ByVal re As RechteckByVal) 
      re.UmfangEinheit("mm", 1) 
      re.a = 1 
    End Sub

    Sub UmfängeByVal() 
      Dim re As RechteckByVal = New RechteckByVal () 
      re.a = 7 : re.b = 3 
      UmfangNeu(re) : UmfangNeu(re) : Console.WriteLine() 
      UmfangÄndern(re) : UmfangÄndern(re) : Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Die Ausgabe zeigt, dass eine Zuweisung eines neuen Wertes sich nicht auf den übergebenen Parameter auswirkt. Eine Änderung des Objekts über den Parameter wirkt sich jedoch aus: Der Umfang ändert sich.

Umfang 20.000  Umfang 20.000 
Umfang 20.0 mm Umfang 8.0 mm

Der Grund für die letzte Änderung liegt daran, dass der übergebene Parameter ein Referenztyp ist. Er speichert einen Verweis auf die eigentlichen Daten, das Rechteck. Egal ob dieser Verweis (Zeiger) kopiert wird oder nicht, er zeigt immer auf die Originaldaten. Unter welchem Namen Daten angesprochen werden, spielt keine Rolle. Das haben wir bereits in Abschnitt 3.1.9, »Datentypen«, gesehen, wo dasselbe Auto unter den Namen Ente und ZweiCV angesprochen wurde. Bei der Neuzuweisung in UmfangNeu dagegen werden die Originaldaten einfach in UmfangNeu dadurch unerreichbar gemacht, dass die kopierte Referenz auf mit New neu erzeugte Daten zeigt.


Hinweis
Mit einem Referenzparameter hat man Zugriff auf die Originaldaten, egal ob der Parameter selbst nur eine Kopie ist oder nicht.


Referenzübergabe mit ByRef (call by reference)

Wollen Sie mit einem Parameter nicht nur Daten eines Referenztyps ändern, sondern die Daten auch in der aufrufenden Methode komplett ersetzen, kommt der Modifikator ByRef ins Spiel. Ein Parameter dieses Typs hat folgende Syntax:


ByRef name As typ


Hinweis
Die Referenzübergabe erlaubt es einer Prozedur (Sub), überhaupt einen Wert »zurückzugeben«, und erlaubt es einer Funktion (Function), mehr als einen Wert »zurückzugeben«.


Diese Deklaration hat den gleichen Effekt wie folgende Deklaration auf Klassenebene (die aufrufende Methode verwendet diese Variable statt einer lokalen):

Private name As typ = aktuellerParameter

Damit Änderungen in der aufrufenden Methode sichtbar sind, muss der übergebene Parameter eine Variable sein. Ein Literal ist prinzipbedingt nicht änderbar. Das folgende Codefragment protokolliert in den Prozeduren UmfangLogByVal und UmfangLogByRef die Anzahl der Umfangsausgaben in einer ganzzahligen Variablen und verwendet die Varianten ByVal und ByRef, um den Unterschied deutlich zu machen. Die letzte Prozedur zeigt den Effekt, wenn eine Referenzvariable mit ByRef übergeben wird.


'...\Klassendesign\Methoden\ByRef.vb

Option Explicit On 
Namespace Klassendesign 
  Class RechteckByRef 
    Public a, b As Double 
    Sub Umfang() 
      Console.Write("Umfang ", 2 * (a + b)) 
    End Sub

    Sub UmfangLogByVal(ByVal no As Integer) 
      Umfang() : no += 1 
    End Sub

    Sub UmfangLogByRef(ByRef no As Integer) 
      Umfang() : no += 1 
    End Sub

    Sub UmfängeUndErsatz(ByRef re() As RechteckByRef) 
      For Each r As RechteckByRef In re 
        r. Umfang() 
      Next 
      re = New RechteckByRef(0) {re(0)} 
    End Sub 
  End Class 
  ... 
End Namespace

Den Zählmethoden wird eine lokale Variable übergeben, und anschließend wird deren Wert protokolliert. Die Methode UmfängeUndErsatz, die das Datenfeld ändert, auf das die Referenzvariable res zeigt, wird zweimal aufgerufen, um den Effekt zu sehen. Der letzte Aufruf protokolliert den Fall, dass eine Konstante übergeben wird. Datenfelder wurden in Abschnitt 2.11, »Datenfelder (Arrays)«, besprochen.


'...\Klassendesign\Methoden\ByRef.vb

Option Explicit On 
Namespace Klassendesign 
  ... 
  Module ByReference 
    Sub UmfängeByRef() 
      Dim re As RechteckByRef = New RechteckByRef() 
      Dim res() As RechteckByRef = {re, New RechteckByRef()} 
      Dim no As Integer = 0 
      Const nr As Integer = 0 
      re.a = 7 : re.b = 3 
      re.UmfangLogByVal(no) : Console.WriteLine("no {0}", no) 
      re.UmfangLogByRef(no) : Console.WriteLine("no {0}", no) 
      re.UmfängeUndErsatz(res) : re.UmfängeUndErsatz(res) 
      Console.WriteLine() 
      re.UmfangLogByRef(nr) : Console.WriteLine("nr {0}", nr) 
      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Die ersten beiden Ausgaben zeigen, dass nur die mit ByRef übergebene Variable permanent geändert wird. Die dritte Ausgabe zeigt, dass die mit ByRef übergebene Referenzvariable auf ein neues Objekt zeigt. UmfängeUndErsatz gibt erst die Umfänge der zwei Rechtecke in res aus und weist ein neues Feld mit nur einem Rechteck zu, sodass im zweiten Aufruf nur ein Umfang ausgegeben wird. Die letzte Ausgabe schließlich macht auf einen unerwarteten Umstand aufmerksam: Der Versuch, eine Konstante zu ändern, wird schlichtweg ignoriert, ohne dass es zu einer Fehlermeldung kommt:

Umfang 20  no 0 
Umfang 20  no 1 
Umfang 20  Umfang 0  Umfang 20 
Umfang 20  nr 0

Hinweis
Änderungen einer Konstanten (zum Beispiel eines Literals oder einer Funktionsrückgabe), die als ByRef-Parameter übergeben wurde, erzeugen keinen Fehler, sondern werden ignoriert, sodass Sie an dieser Stelle selbst die korrekte Logik sicherstellen müssen.


Das folgende Codefragment demonstriert, dass innerhalb einer Methode korrekte Parameter vorliegen – auch für den Aufruf mit einer Konstanten.


'...\Klassendesign\Methoden\ByRef.vb

Option Explicit On 
Namespace Klassendesign 
  ... 
  Module ByRefKonstante

    Sub Inkrement(ByRef no As Integer) 
      no += 1 
      Console.WriteLine("Inkrement: {0}", no) 
    End Sub

    Sub Test() 
      Inkrement(7) 
      Console.ReadLine() 
    End Sub

  End Module 
End Namespace

Die Ausgabe zeigt, dass der Parameter innerhalb der Methode Inkrement() richtig verwendet wird, es wird nur bei der Rückgabe keine Referenz geändert.

Inkrement: 8

Hinweis
Ein aktueller Wert eines ByRef-Parameters, der beim Aufruf der Methode mit CType konvertiert wurde, verhält sich wie eine Konstante und wird nicht geändert.


Arrays als Parameter

Für den Zugriff auf ein Array reicht eine Referenz auf die Daten. Klammern und Indizes werden nur gebraucht, wenn es darum geht, entweder in der Deklaration die Struktur des Arrays festzulegen oder beim Zugriff nur Teile des Arrays anzusprechen. Das Array als Ganzes hat keine Klammern. Dies schlägt sich auch in Methodenparametern nieder, die ein Array verwenden. Das nächste Codefragment zeigt hierzu den Zugriff auf ein Feld von zweidimensionalen Matrizen. Der ersten Methode wird ein Feld (erstes Klammerpaar) von Matrizen (zweites Klammerpaar) übergeben, der zweiten nur eine einzelne Matrix. Die erste Methode pickt sich aus dem Feld von Matrizen die gewünschte heraus und ruft die zweite Methode auf. In dieser erfolgt der Zugriff auf das Element der Matrix.


'...\Klassendesign\Methoden\Arrayparameter.vb

Option Explicit On 
Namespace Klassendesign

  Class Matrizenfeld 
    Sub Element(ByVal daten()(,) As Double, _ 
                ByVal no As Byte, ByVal zeile As Byte, ByVal spalte As Byte) 
      Element(daten(no), zeile, spalte) 
    End Sub

    Sub Element(ByVal matrix(,) As Double, _ 
                ByVal zeile As Byte, ByVal spalte As Byte) 
      Console.WriteLine("Element aus Matrix {0}", matrix(zeile, spalte)) 
    End Sub 
  End Class 
  ... 
End Namespace

Beim Aufruf der ersten Methode muss hinter dem Arraynamen kein Klammerpaar angegeben werden, da das Array als Ganzes übergeben wird. Die Methode pickt sich selbst die gewünschte Matrix heraus. Beim zweiten Aufruf findet die Selektion bereits hier statt, und das Klammerpaar hinter dem Arraynamen selektiert die Matrix.


'...\Klassendesign\Methoden\Arrayparameter.vb

Option Explicit On 
Namespace Klassendesign 
  ... 
  Module Matrizen 
    Sub Test()

      Dim daten(1)(,) As Double 
      daten(0) = New Double(,) {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}} 
      daten(1) = New Double(,) {{11, 12, 13}, {14, 15, 16}, {17, 18, 19}}

      Dim m As Matrizenfeld = New Matrizenfeld() 
      m.Element(daten, 1, 0, 2) 
      m.Element(daten(1), 0, 2)

      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Die Ausgabe beider Methoden ist identisch und zeigt, dass beim Elementzugriff (unterste Ebene des Arrays) ausreichend Klammern und Indizes spezifiziert werden müssen. Wie diese auf den Aufruf und die Logik innerhalb der Methode verteilt werden, ist eine Frage des Programmierstils und richtet sich nach Ihren Erfordernissen. Dabei ist zu beachten, dass die Datentypen in der Methodendeklaration formell zu den Aufrufparametern passen müssen.

Element aus Matrix 13 
Element aus Matrix 13

Optionales: Optional und ParamArray

Die verschiedenen Arten der Ausgabe eines Umfangs in unterschiedliche Methoden zu codieren, ist recht umständlich. Wenn wie in diesem Fall nicht übergebene Parameter automatisch im Quelltext durch feste Werte ersetzt werden, können Sie durch Verwendung optionaler Parameter das Ganze in einer einzigen Methode unterbringen. Jeder optionale Parameter kann übergeben werden, muss es aber nicht. Fehlt der Parameter, wird er durch einen von Ihnen spezifizierten Wert ersetzt, und die Methode wird so aufgerufen, als habe man alle Parameter übergeben. Das heißt also, dass innerhalb der Methode nicht entschieden werden kann, ob ein optionaler Parameter explizit angegeben wurde oder ob der Standardwert verwendet wird. Damit geht einher, dass die Anweisungen innerhalb einer Methode komplett unabhängig davon sind, ob ein Parameter optional ist oder nicht und ob ein optionaler Parameter explizit angegeben wurde. Die Spezifikation hat folgende Syntax (Alternativen sind durch | getrennt, kursive Teile müssen Sie Ihren Bedürfnissen anpassen):


Optional ByRef|ByVal name As typ = wert

Die Verwendung optionaler Parameter unterliegt einigen Beschränkungen:

  • Optionale Parameter müssen am Ende der Parameterliste stehen.
  • Eine Kombination mit ParamArray ist nicht möglich.
  • Beim Aufruf dürfen keine (optionalen) Parameter übersprungen werden.
  • wert muss zur Compilezeit festliegen.
  • wert muss zuweisungskompatibel zu typ sein.
  • typ darf keine Struktur sein (siehe Abschnitt 4.2, »Strukturen«).

Hinweis
Methoden mit optionalen Parametern werden behandelt, als hätten sie gleichzeitig mehrere Signaturen (siehe Abschnitt 3.3.1, »Prinzip«). Jede unterschiedliche Kombination an verwendeten und weggelassenen optionalen Parametern repräsentiert eine Signatur.


Wir definieren eine Klasse mit einer Methode, die die verschiedenen Arten der Ausgabe zusammenfasst.


'...\Klassendesign\Methoden\Optional.vb

Option Explicit On 
Namespace Klassendesign 
  Class RechteckOpt 
    Public a, b As Double

    Sub UmfangAusgabe( _ 
      Optional ByVal e As String = "", Optional ByVal s As Integer = 4) 
      Dim form As String = "Umfang {0:F" & s & "} {1} " 
      Console.Write(form, 2 * (a + b), e) 
    End Sub

  End Class 
  ... 
End Namespace

Der Umfang wird nun über einen einzigen Methodennamen ausgegeben. Der auskommentierte Aufruf würde einen Parameter überspringen und ist daher verboten.


'...\Klassendesign\Methoden\Optional.vb

Option Explicit On 
Namespace Klassendesign 
  ... 
  Module Optionale

    Sub Umfang() 
      Dim re As RechteckOpt = New RechteckOpt() 
      re.a = 7 : re.b = 3 
      re.UmfangAusgabe() 
      re.UmfangAusgabe("mm") 
      're.UmfangAusgabe(2) 
      re.UmfangAusgabe("mm", 2) 
      Console.ReadLine() 
    End Sub

  End Module 
End Namespace

Eine weitere Variante optionaler Parameter ist durch die Deklaration mit ParamArray mit folgender Syntax gegeben (ByRef ist nicht erlaubt):


ByVal ParamArray name() As typ = wert

Ein derartiger Parameter repräsentiert null oder mehr Werte vom angegebenen Typ oder ein eindimensionales Feld solcher Werte. Eine Mischung von Werten und Feldern ist nicht erlaubt. Innerhalb der Methode ist ein solcher Parameter immer ein eindimensionales Feld vom angegebenen Typ. Wird beim Aufruf kein Wert für den Parameter angegeben, hat das Feld die Länge null. Ein eindimensionales Feld wird einfach durchgereicht (genauer: die Kopie des Verweises auf das Feld). Ansonsten werden die übergebenen Parameter in einem neu erstellten eindimensionalen Feld zusammengefasst, und der Verweis auf dieses Feld wird dann als Parameter übergeben.

Das folgende Codefragment definiert eine Klasse mit einer Methode zum Zählen von Rechtecken:


'...\Klassendesign\Methoden\ParamArray.vb

Option Explicit On 
Namespace Klassendesign 
  Class RechteckPA

    Sub Zählen(ByVal ParamArray re() As RechteckPA) 
      Console.WriteLine("{0} Rechtecke", re.Length) 
    End Sub

  End Class 
  ... 
End Namespace

Die Zählmethode kann mit einer beliebigen Anzahl an Rechtecken (siehe die ersten drei Aufrufe) oder mit einem Feld von Rechtecken (vierter Aufruf) aufgerufen werden. Die beiden auskommentierten Aufrufe sind verboten, da sie versuchen, Felder mit anderen Werten zu kombinieren.


'...\Klassendesign\Methoden\ParamArray.vb

Option Explicit On 
Namespace Klassendesign 
  ... 
  Module ParamArrays

    Sub Anzahl() 
      Dim re As RechteckPA = New RechteckPA() 
      Dim res() As RechteckPA = {re, re} 
      re.Zählen() 
      re.Zählen(re) 
      re.Zählen(re, re) 
      re.Zählen(res) 
      're.Zählen(res, res) 
      're.Zählen(re, res) 
      Console.ReadLine() 
    End Sub

  End Module 
End Namespace

Hinweis
Nur ein Parameter darf mit ParamArray gekennzeichnet werden, und er muss als Letzter in der Parameterliste stehen. Er darf nicht mit optionalen Parametern kombiniert werden.


Lokale Variablen und Qualifizierung mit Me

Wenn eine lokale Variable denselben Namen hat wie eine Variable auf Klassenebene, verdeckt die lokale Variable die andere gleichen Namens. Die Objektvariable kann dann durch ihren vollqualifizierten Namen objekt.var angesprochen werden. Damit Sie nicht immer selbst eine Variable mit einer Objektreferenz vorhalten müssen, hat Visual Basic den Bezeichner Me, der auf das gerade aktuelle Objekt zeigt. Das folgende Codefragment zeigt, dass Verdeckung und Qualifizierung gleichermaßen für die lokale Variable b und den Parameter a der Methode gelten (sie sind lokal zur Methode).


'...\Klassendesign\Methoden\Me.vb

Option Explicit On 
Namespace Klassendesign 
  Class RechteckMe

    Public a, b As Double

    Sub QuaderVolumenAusgeben(ByVal a As Double) 
      Dim b As String = "mm^3" 
      Console.WriteLine("Vol {0} {1}", Me.a * Me.b * a, b) 
    End Sub

  End Class 
  ... 
End Namespace

Die aufrufende Methode merkt nichts von der Qualifizierung.


'...\Klassendesign\Methoden\Me.vb

Option Explicit On 
Namespace Klassendesign 
  ... 
  Module Qualifizierung

    Sub MitMe() 
      Dim re As RechteckMe = New RechteckMe() 
      re.a = 7 : re.b = 3 
      re.QuaderVolumenAusgeben(10) 
      Console.ReadLine() 
    End Sub

  End Module 
End Namespace

Die Ausgabe zeigt, dass die richtigen Werte verwendet wurden (7*3*10):

Vol 210 mm^3

Hinweis
Wenn Sie es übersichtlicher finden, dürfen Sie alle Klassenmitglieder mit Me qualifizieren, auch wenn es gar keinen Namenskonflikt gibt.



Rheinwerk Computing - Zum Seitenanfang

3.3.4 Überladung (Overloads) Zur nächsten ÜberschriftZur vorigen Überschrift

Bei der enormen Funktionalität, die .NET bietet, ist eine gute Organisation dringend erforderlich. Die Einteilung in Namensräume haben Sie bereits in Abschnitt 2.4.1, »Namensräume und Imports«, kennengelernt. Ohne weitere Maßnahmen wäre trotz dieser Organisation die Liste an Methoden viel länger, als sie in der Realität ist. Vielleicht ohne sich dessen bewusst zu sein, haben Sie schon oft ein weiteres Konzept genutzt:


Überladung: Definition verschiedener Methodendefinitionen gleichen Namens, aber verschiedener Signatur (siehe Abschnitt 3.3.1, »Prinzip«).



Hinweis
Die Parameterlisten überladener Methoden dürfen sich nicht nur durch ByRef, ByVal, Optional oder ParamArray unterscheiden, da sie nicht Teil der Signatur sind (Abschnitt 3.3.1, »Prinzip«).


Damit braucht man sich für alle Arten, mit denen eine Methode aufgerufen werden kann, nur einen Namen zu merken. Die Methode Console.WriteLine zum Beispiel besteht aus vielen überladenen Signaturen. Es gibt eine leere, für jeden primitiven Datentyp eine und aus Performance-Gründen neben einer formatierten mit einer Objektliste einige mit Einzelobjekten.

Sub WriteLine() 
Sub WriteLine(ByVal value As Boolean) 
Sub WriteLine(ByVal value As Char) 
Sub WriteLine(ByVal buffer As Char()) 
Sub WriteLine(ByVal buffer As Char(), _ 
  ByVal index As Integer,ByVal count As Integer) 
Sub WriteLine(ByVal value As Decimal) 
Sub WriteLine(ByVal value As Double) 
Sub WriteLine(ByVal value As Single) 
Sub WriteLine(ByVal value As Integer) 
Sub WriteLine(ByVal value As UInteger) 
Sub WriteLine(ByVal value As Long) 
Sub WriteLine(ByVal value As ULong) 
Sub WriteLine(ByVal value As Object) 
Sub WriteLine(ByVal value As String) 
Sub WriteLine(ByVal format As String,ByVal arg0 As Object) 
Sub WriteLine(ByVal format As String, _ 
  ByVal arg0 As Object, ByVal arg1 As Object) 
Sub WriteLine(ByVal format As String, ByVal arg0 As Object, _ 
  ByVal arg1 As Object, ByVal arg2 As Object) 
Sub WriteLine(ByVal format As String, _ 
  ByVal arg0 As Object,ByVal arg1 As Object, _ 
  ByVal arg2 As Object,ByVal arg3 As Object) 
Sub WriteLine(ByVal format As String, ByVal ParamArray arg As Object())

Schauen wir uns ein Anwendungsbeispiel an, bei dem die Überladung den Komfort erhöht. Eine Methode mit optionalen Parametern hat eine festgelegte Reihenfolge der Parameter. Als Konsequenz kann man beim Aufruf der Methode Parameter nur am Ende der Parameterliste auslassen. Wird zum Beispiel eine Methode, die einen optionalen Integer- und einen optionalen String-Parameter erwartet, nur mit einem String aufgerufen, passt dieser – als erster Parameter – nicht zum erwarteten Integer-Typ. Dass ein Parameter optional ist, ändert nichts am Erfordernis korrekter Parametertypen.

Mit den Möglichkeiten der Überladung können Sie das Problem der beliebigen Parameterreihenfolge bei optionalen Parametern lösen, indem Sie eine weitere Definition mit anderer Reihenfolge ergänzen. Das folgende Codefragment definiert eine Klasse mit einer Flächenausgabe. Eine Überladung ohne optionale Parameter ist genauso möglich, erfordert aber mehr verschiedene Methodensignaturen.


'...\Klassendesign\Methoden\Overloads.vb

Option Explicit On 
Namespace Klassendesign 
  Class RechteckOv 
    Public a, b As Double

    Overloads Sub FlächeAusgabe() 
      FlächeAusgabe(0) 
    End Sub

    Overloads Sub FlächeAusgabe( 
      ByVal s As Short, Optional ByVal e As String = "" _ 
    ) 
      FlächeAusgabe(e, s) 
    End Sub

    Overloads Sub FlächeAusgabe( _ 
      ByVal e As String, Optional ByVal s As Short = 1 _ 
    ) 
      Dim form As String = "Fläche {0:F" & s & "}{1}. " 
      Console.Write(form, a * b, e) 
    End Sub 
  End Class 
  ... 
End Namespace

Hinweis
Obwohl die Kennzeichnung überladener Methoden mit Overloads nur optional ist, sollten Sie sie immer verwenden. Bei einer Aufteilung der Klassendefinitionen in mehrere Dateien sehen Sie nicht alle Methoden und wissen dann trotzdem, dass Überladung eingesetzt wird. Spätestens im Rahmen der Vererbung macht es das Leben viel leichter (siehe Abschnitt 3.13.4, »Modifikation: Shadows und Overloads«).



Hinweis
Leider erlaubt es der Compiler, eine Methode mit Overloads zu kennzeichnen, auch wenn keine zweite Signatur existiert.


Nun dürfen die Parameter in beliebiger Reihenfolge angegeben oder ausgelassen werden:


'...\Klassendesign\Methoden\Overloads.vb

Option Explicit On 
Namespace Klassendesign 
  ... 
  Module Überladen 
    Sub Methoden() 
      Dim re As RechteckOv = New RechteckOv() 
      re.a = 7 : re.b = 3 
      re.FlächeAusgabe() 
      re.FlächeAusgabe("mm^2") 
      re.FlächeAusgabe(3) 
      re.FlächeAusgabe("mm^2", 2) 
      Console.ReadLine() 
    End Sub 
    ... 
  End Module 
End Namespace

Die Werte der aktuellen Parameter sind so gewählt, dass man in der Ausgabe die aufgerufene Methode eindeutig identifizieren kann. Die erste Ausgabe ohne Nachkommastellen wurde vom parameterlosen Aufruf erzeugt. Dem nächsten Aufruf fehlt die Angabe der Nachkommastellen, sodass der optionale Parameter 1 der dritten Definition von FlächeAusgeben verwendet und nur eine Nachkommastelle ausgegeben wird. Beim dritten Aufruf wird keine Einheit angegeben, und es wird auch keine ausgegeben, da der optionale Parameter der verwendeten zweiten Methodendefinition ein Leerstring ist. Die letzte Ausgabe schließlich wird durch die letzte Methodendefinition erzeugt, da sie die richtige Reihenfolge der Parametertypen hat.

Fläche 21. Fläche 21.0mm^2. Fläche 21.000. Fläche 21.00mm^2.

Hinweis
Neben Methoden dürfen nur noch Objektkonstruktoren (siehe Abschnitt 3.5, »Objektinitialisierung mit Konstruktoren«), Operatoren (siehe Abschnitt 3.11, »Benutzerdefinierte Operatoren«) und Eigenschaften (siehe Abschnitt 3.7, »Eigenschaften«) überladen werden.


Implizite Typumwandlung

Ganz ohne Arbeit erhalten Sie noch eine weitere Art der Überladung. Beim Aufruf einer Methode wird zuerst nach der am besten passenden Definition gesucht. Wird keine perfekt passende Definition gefunden, wird versucht, durch implizite Typumwandlungen etwas Passendes zu finden. Durch diesen Mechanismus repräsentiert eine Methode außer ihrer exakten Signatur auch all die Signaturen, die durch eine implizite Umwandlung diese exakte Signatur erreichen können (zu Signaturen siehe Abschnitt 3.3.1, »Prinzip«).

In Abschnitt 2.5.7, »Datentypkonvertierung«, haben wir bereits einige dieser impliziten Umwandlungen kennengelernt. Alle diese Umwandlungen lassen sich in zwei Gruppen einteilen:

  • Typkompatibilität: Wenn der Operator TypeOf objekt Is typ True ergibt, ist keine Umwandlung nötig, da bereits ein Typ vorliegt, der typ repräsentieren kann (zur Vererbung siehe Abschnitt 3.13.1, »Klassenbeziehung durch Inherits«).
  • Umwandelbarkeit: Der Datentyp definiert eine implizite Umwandlung in den gewünschten Typ. Abschnitt 2.5.7, »Datentypkonvertierung«, behandelt die primitiven Datentypen, eigene Definitionen werden in Abschnitt 3.11.4, »Typumwandlung mit CType: Widening und Narrowing«, behandelt.

Hinweis
Die Typkompatibilität bezieht sich auch auf Datentypen mit Typparametern (siehe generische Datentypen in Abschnitt 4.4.3, »Vererbung und Überladung«).


Auf die Typkompatibilität und benutzerdefinierte Umwandlungen gehen wir später ein. Der Effekt, dass durch implizite Umwandlung weitere Methodensignaturen automatisch erschlossen werden, bleibt auch dort derselbe. Es reicht also, am Beispiel der primitiven Datentypen den Mechanismus zu verstehen. Im folgenden Codefragment wird die Methode FlächeAusgabe des vorigen Abschnitts mit einer Variablen vom Typ Byte aufgerufen, obwohl sie ein Short erwartet:


'...\Klassendesign\Methoden\Overloads.vb

Option Explicit On 
Namespace Klassendesign 
  Module Überladen 
    ... 
    Sub ImpliziteUmwandlung() 
      Dim re As RechteckOv = New RechteckOv() 
      re.a = 7 : re.b = 3 
      Dim stellen As Byte = 5 
      re.FlächeAusgabe(stellen) 'erwartet Short 
      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Die Ausgabe zeigt die korrekte Verwendung des Parameters, der beim Aufruf implizit in einen Short umgewandelt wird:

Fläche 21.00000.

Explizite Typumwandlung

Passt der Datentyp eines Parameters nicht genau und ist eine implizite Umwandlung nicht möglich, scheitert der Compiler wie im folgenden Codefragment. Die Methode erwartet einen Wert vom Typ Short, bekommt aber einen Integer. Da die Umwandlung von Integer nach Short Datenverluste bedeuten könnte, kann sie nicht implizit erfolgen.

Dim re As RechteckOv = New RechteckOv() 
Dim stellen As Integer = 5 
re.FlächeAusgabe(stellen) 'erwartet Short -> Fehler!!

Eine explizite Typumwandlung löst das Problem, wie das nächste Codefragment zeigt:


'...\Klassendesign\Methoden\Overloads.vb

Option Explicit On 
Namespace Klassendesign 
  Module Überladen 
    ... 
    Sub ExpliziteUmwandlung() 
      Dim re As RechteckOv = New RechteckOv() 
      re.a = 7 : re.b = 3 
      Dim stellen As Integer = 5 
      re.FlächeAusgabe(CType(stellen,Short)) 'erwartet Short 
      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Rheinwerk Computing - Zum Seitenanfang

3.3.5 Rückgabewert Zur nächsten ÜberschriftZur vorigen Überschrift

Um die Beispiele einfach zu halten, haben wir bisher meistens nur Prozeduren ohne Rückgabewert definiert. Soll eine Methode einen Wert zurückgeben, muss sie als Function und nicht als Sub deklariert werden – mit Angabe des Datentyps des zurückgelieferten Wertes.


Der Typ des Rückgabewertes ist beliebig, inklusive Arrays und eigener Klassen. Innerhalb der Methode kann der Rückgabewert auf zwei Arten festgelegt werden:


  • Zuweisung eines Wertes an den Funktionsnamen
  • Explizite Return-Anweisung

Hinweis
Anweisungen hinter Return oder Exit im selben Block sind unerreichbar. Der Compiler erzeugt keine Warnmeldung.


Welche der beiden Varianten Sie verwenden, ist eher Geschmackssache. Ich bevorzuge die Variante mit Return, da sie den Ausstiegspunkt und die Festlegung des Rückgabewertes an einer Stelle zusammenfasst. Außerdem ist sie in anderen Programmiersprachen verbreiteter, und das Wort Return ist leichter im Quelltext zu finden als eine gewöhnliche Zuweisung.

Als Beispiel zeigt das nächste Codefragment eine Flächenberechnung. Ist die Fläche null, wird die Funktion direkt verlassen. Im Fall langer Flächen wird vorher noch ein Wert an Fläche zugewiesen. Ansonsten wird die Funktion mit Return verlassen.


'...\Klassendesign\Methoden\Function.vb

Option Explicit On 
Namespace Klassendesign 
  Class RechteckFun 
    Public a, b As Double

    Function Fläche() As Double 
      If a * b = 0 Then Exit Function 
      If a > 5 Then 
        Fläche = a * b 
        Exit Function 
      End If 
      Return a * b 
    End Function

  End Class 
  ... 
End Namespace

Hinweis
Der Rückgabewert einer Funktion darf ignoriert werden.


Die Aufrufe berücksichtigen alle drei implementierten Fälle des Ausstiegs.


'...\Klassendesign\Methoden\Function.vb

Option Explicit On 
Namespace Klassendesign 
  ... 
  Module Funktion 
    Sub Rückgabewerte() 
      Dim re As RechteckFun = New RechteckFun() 
      re.a = 7 
      Console.Write("Fläche {0}. ", re.Fläche()) '0 
      re.b = 3 
      Console.Write("Fläche {0}. ", re.Fläche()) '21 
      re.a = 3 
      Console.Write("Fläche {0}. ", re.Fläche()) '9 
      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Die erste Ausgabe zeigt, dass ein »sofortiger« Ausstieg bedeutet, dass 0 zurückgegeben wird. Es ist die 0, die der Initialisierung des Datentyps des Rückgabewertes (Double) entspricht (siehe Abschnitt 2.5.6, »Initialisierung von Variablen«). Beim zweiten Aufruf ist die zweite If-Bedingung in Fläche erfüllt, und dem Funktionsnamen wird vor dem Ausstieg aus der Methode der Wert 7*3 zugewiesen. Der letzte Fall schließlich endet in einem ganz normalen Return 3*3.

Fläche 0. Fläche 21. Fläche 9.

Hinweis
Der Compiler gibt keine Warnmeldung, wenn die Festlegung eines Rückgabewertes unterbleibt. Die Funktion gibt dann eine dem Datentyp der Rückgabe entsprechende »Null« zurück (siehe Abschnitt 2.5.6, »Initialisierung von Variablen«).


Referenzen

Bisher haben wir der Einfachheit halber nur Zahlen zurückgegeben. Der Typ der Rückgabe ist aber beliebig. Ist das Funktionsergebnis eine Referenz vom selben Typ, lassen sich einfach mehrere Aktionen hintereinanderschalten. Der Preis für die Bequemlichkeit bei der Nutzung ist die etwas kompliziertere Formulierung der Klasse. Das nächste Codefragment zeigt dazu eine Klasse mit einer Liste von Werten. Die Methoden Größer() und Kleiner() werfen über die Methode Sel() Werte aus der Liste werte raus. Damit die Verkettung funktioniert, gibt Sel() mit Me eine Referenz auf Auswahl zurück:


'...\Klassendesign\Methoden\Referenzen.vb

Option Explicit On 
Namespace Klassendesign 
  Class Auswahl 
    Private w() As Short 
    Sub New(ByVal werte() As Short) 
      Me.w = werte 
    End Sub

    Private Function Sel(ByVal lim As Double,ByVal gr As Boolean) As Auswahl 
      Dim i As Integer = –1 
      For no As Int32 = 0 To w.Length – 1 
        If If(gr, w(no)>lim, w(no)<lim) Then : i+=1 : w(i)=w(no) : End If 
      Next 
      ReDim Preserve w(i) 
      Return Me 
    End Function

    Function Größer(ByVal grenze As Double) As Auswahl 
      Return Sel(grenze, True) 
    End Function

    Function Kleiner(ByVal grenze As Double) As Auswahl 
      Return Sel(grenze, False) 
    End Function

    Sub Ausgeben() 
      For Each wert As Double In w : Console.Write(wert & " ") : Next 
    End Sub

  End Class 
  ... 
End Namespace

Die Mühe zahlt sich bei der Verwendung aus. Das Durchreichen der Referenz ermöglicht eine sehr knappe Formulierung der Verkettung von Aufrufen:


'...\Klassendesign\Methoden\Referenzen.vb

Option Explicit On 
Namespace Klassendesign 
  ... 
  Module Referenzen 
    Sub Verkettung() 
      Dim werte() As Short = {17, 2, 5, 8, 86, 26, 14, 36, 22, 101, 32, 42} 
      Dim aus As New Auswahl(werte)

      aus.Größer(10).Kleiner(50).Ausgeben()

      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Nur die Zahlen im spezifizierten Intervall werden ausgegeben:

17 26 14 36 22 32 42

Rheinwerk Computing - Zum Seitenanfang

3.3.6 Reine Deklaration mit Partial Zur nächsten ÜberschriftZur vorigen Überschrift

Um die Übersichtlichkeit von Klassendefinitionen etwas zu erhöhen, die mit Partial in mehreren Teilen erfolgt, können Methodensignaturen ohne Methodenrumpf auch mit Partial gekennzeichnet werden und in einem anderen Klassenteil implementiert werden. Dabei unterliegt dieses Konstrukt einigen Beschränkungen:

  • Dieselbe Methodensignatur darf nur an einer Stelle in allen Teilen einer Klassendefinition mit Partial gekennzeichnet werden.
  • Die Methode muss mit Private gekennzeichnet werden.
  • Nur Prozeduren ohne Rückgabewert (Sub) sind erlaubt.
  • Attribute und Ereignishandler der Partial-Deklaration und der Implementation werden zusammengefasst.

Das nächste Codefragment zeigt die Verwendung des Konzepts. Die Implementation von Platte fehlt absichtlich. Die Methoden werden mit einem Parameter namens Zehn() aufgerufen, der sich durch eine Ausgabe bemerkbar macht, um prüfen zu können, ob der Parameter überhaupt ausgewertet wurde.


'...\Klassendesign\Methoden\Function.vb

Option Explicit On 
Namespace Klassendesign

  Partial Public Class RechteckP 
    Public a, b As Double

    Partial Private Sub Platte(ByVal h As Short) 
    End Sub

    Partial Private Sub Quader(ByVal h As Short) 
    End Sub

    Function Zehn() As Short 
      Console.Write("h=10 ") 
      Return 10 
    End Function 
  End Class

  Partial Public Class RechteckP

    Private Sub Quader(ByVal h As Short) 
      Console.WriteLine("Quader {0} ", a * b * h) 
    End Sub

    Sub QuaderVolumenDrucken() 
      Platte(Zehn()) 
      Quader(Zehn()) 
      Console.ReadLine() 
    End Sub

  End Class

  Module Partiell 
    Sub Aufruf() 
      Dim re As RechteckP = New RechteckP() 
      re.a = 7 : re.b = 3 
      re.QuaderVolumenDrucken() 
    End Sub 
  End Module 
End Namespace

Die Ausgabe zeigt, dass die Methode Quader() wie gewünscht arbeitet und dass die nicht definierte Methode Platte komplett ignoriert wurde. Selbst der Parameter des Aufrufs wurde nicht ausgewertet (der Parameter Zehn() hätte sonst eine Ausgabe erzeugt).

h=10 Quader 210

Rheinwerk Computing - Zum Seitenanfang

3.3.7 Grafikbibliothek: Zugriffsmethoden auf die Größe des Rechtecks topZur vorigen Überschrift

In diesem Abschnitt erweitern wir die in Abschnitt 3.1.12, »Grafikbibliothek: Beispiel für Kapitel 3 und 4«, eingeführte und in Abschnitt 3.2.6, »Grafikbibliothek: private Größe des Rechtecks«, erweiterte Grafikanwendung. Damit die privaten Dimensionen a und b des Rechtecks von außen nutzbar werden, fügen wir der Klasse Rechteck ein paar Methoden hinzu. Sie nutzen Überladung mit Overloads, einen optionalen Parameter, Referenzübergabe mit ByRef und die Rückgabe eines Wertes mit Return in einer Funktion.


'...\Klassendesign\Graphik\Methoden.vb

Option Explicit On 
Namespace Klassendesign 
  Partial Public Class Rechteck

    Overloads Sub Größe() 
      Console.WriteLine("Dimension des Rechtecks: {0}x{1}", a, b) 
    End Sub

    Overloads Sub Größe(ByVal a As Double, Optional ByVal b As Double = 1) 
      If a <= 0 Then Throw New ArgumentException("Negative Länge!") 
      If b <= 0 Then Throw New ArgumentException("Negative Breite!") 
      Me.a = a : Me.b = b 
    End Sub

    Function UmfangUndFläche(ByRef fläche As Double) As Double 
      fläche = a * b 
      Return 2 * a + 2 * b 
    End Function

  End Class 
End Namespace

Zum Testen definieren wir ein Rechteck, lesen die Größe aus, setzen sie und lesen sie erneut aus. Schließlich berechnen wir Umfang und Fläche und geben beide in getrennten Schreibbefehlen aus.


'...\Klassendesign\Zeichner\Methoden.vb

Option Explicit On 
Namespace Klassendesign 
  Partial Class Zeichner 
    Sub Methoden() 
      Dim fläche As Double 
      Dim rechteck As Rechteck = New Rechteck()

      rechteck.Größe() 
      rechteck.Größe(7) : rechteck.Größe() 
      rechteck.Größe(7, 8) : rechteck.Größe()

      Console.WriteLine("Umfang: {0}.", rechteck.UmfangUndFläche(fläche)) 
      Console.WriteLine("Fläche: {0}.", fläche)

    End Sub 
  End Class 
End Namespace

Zur Kontrolle sehen Sie hier die Ausgabe der Methode Methoden():

Dimension des Rechtecks: 1x1 
Dimension des Rechtecks: 7x1 
Dimension des Rechtecks: 7x8 
Umfang: 30. 
Fläche: 56.

Die nächste Erweiterung erfolgt in Abschnitt 3.4.4, »Grafikbibliothek: Rechteckübergreifendes«.



Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

<< zurück
  Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: Visual Basic 2008
Visual Basic 2008
Jetzt Buch bestellen


 Ihre Meinung?
Wie hat Ihnen das Openbook gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Rheinwerk-Shop: Visual Basic 2012






 Visual Basic 2012


Zum Rheinwerk-Shop: Schrödinger programmiert C++






 Schrödinger
 programmiert C++


Zum Rheinwerk-Shop: IT-Handbuch für Fachinformatiker






 IT-Handbuch für
 Fachinformatiker


Zum Rheinwerk-Shop: Professionell entwickeln mit Visual C# 2012






 Professionell
 entwickeln mit
 Visual C# 2012


Zum Rheinwerk-Shop: Windows Presentation Foundation






 Windows Presentation
 Foundation


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo




Copyright © Rheinwerk Verlag GmbH 2009
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern