Im nächsten Abschnitt werden wir uns mit Klassen und Objekten auseinandersetzen. Eine Klasse kann aufgefasst werden als eine Menge von Daten und Funktionen. In der OOP (Objekt orientierten Programmierung) nennt man auch die Daten Eigenschaften und die Funktionen Methoden.
Darüber hinaus kann man in einer Klasse festlegen, welche Daten und Funktionen von außen sichtbar sind und welche nicht. Dies nennt man die Zugriffsteuerung.
Man kann nun eine Instanz einer Klasse erzeugen, die man auch Objekt nennt. Dieses Objekt hat nun die festgelegten Eigenschaften und man kann die festgelegten Methoden auf dieses Objekt anwenden.
Daneben gibt es den sogenannten Polymorphismus. D.h. eine Klasse kann sich ähnlich wie eine andere verhalten und gleichnamige Eigenschaften und Methoden enthalten. So kann es beispielsweise mehrere Datenbanken-Klassen geben, die alle Tabellen beinhalten, auf die wiederum ähnlich zugegriffen werden kann. In jeder Klasse könnte es einen Array von Tabellen geben, jede Datenbank kann geöffnet werden, etc. In C++ kann dies durch Vererbung realisiert werden (man hat eine Basisklasse Datenbank, von denen man jede einzelne Klasse AccessDB, ParadoxDB, etc. ableitet.). In Visual Basic macht man das mit der Definition einer Schnittstelle Datenbank und die Klassen AccessDB, ParadoxDB implementieren dieses Interface.
Der Sinn hinter der OOP ist einmal das Kapseln von Daten und Code. Durch die Zugriffsteuerung kann man Schnittstelle und Implementierung trennen. Mit Hilfe des Polymorphismus kann man Klassen erzeugen, die gegeneinander (auch zu Laufzeit) austauschbar sind. Dies kann natürlich keine erschöpfende Abhandlung der OOP sein. Dazu gibt es aber genügend andere weiterführende Literatur. Diese Einführung kann dazu nur einen Einstieg liefern.

Visuelle Objekte

Visual Basic hat ab der Version 5.0 Grundzüge einer Objekt orientierten Sprache, inklusive Kapselung von Code und Daten, Zugriffsteuerung auf diese Elemente und Polymorphismus.
(In der Version 3.0 gab es praktisch nur visuelle Objekte, ab Version 4.0 konnte man eigene Klassen hinzufügen. Allerdings war da noch kein Polymorphismus möglich. )
Beispiel: Visuelle Klassen
Die folgende Tabelle enthält ein paar wichtige Klassen von visuellen Steuerelementen.

Label Feld zur Anzeige statischer Texte in einer Form (also nicht von Anwender änderbar)
Textfeld Feld zur Anzeige änderbarer Texte in einer Form
Button Aktionsbutton in einem Formular

Der Zugriff auf Eigenschaften und Methoden einer Instanz einer solchen Klasse erfolgt durch:

Objektname.Eigenschaft Schreiben und Lesen möglich (je nach Objekttyp)
Objektname.Methode [(Parameterliste)] Aufruf einer Methode

Beispiel: Zugriff auf Eigenschaften und Methoden

Label1.Caption = "Hallo, Welt" Label1.Refresh

Die erste Zeile verändert die Eigenschaft CAPTION (der angezeigte Text) eines Labels. Die zweite wendet die Methode REFRESH (Neuzeichnen des Objekts) an. Weitere Informationen zu visuellen Objekten sind im Kapitel 2 zu finden. Dort gibt es auch eine Kurzbeschreibung der wichtigsten visuellen Elemente.

Eigene Klassen definieren

Ab Version 4.0 ist nun auch möglich eigene Klassen zu erstellen. Man kann ebenso wie in anderen objekt orientierten Sprache Eigenschaften und Methoden definieren. Es gibt die Prädikate PUBLIC und PRIVATE zur Steuerung des Zugriffs auf diese Elemente von außen. Man kann also eine Eigenschaft oder Methode für alle zugänglich (PUBLIC) oder nur den für internen Gebrauch (PRIVATE) deklarieren.
FALSCH!
Ab der Version 5.0 gibt es überdies das Prädikat FRIEND. Damit erlaubt man den Zugriff anderer Objekte der gleichen Klasse auf dieses Element. Von außen ist der Zugriff nicht erlaubt.
Übersicht zu der Zugriffsteuerng:

Prädikat Zugriff innerhalb des Objekts Zugriff aus anderem Objekt derselben Klasse Zugriff von außen
PRIVATE ja nein nein
FRIEND ja ja nein
PUBLIC ja ja ja

Damit ist es möglich Klassen zu schreiben, die einerseits eine feste Schnittstelle aus öffentlichen Elementen besitzt und die Implementierungsdetails verbergen. Als Benutzer einer Klasse ist es grundsätzlich uninteressiert, wie eine Funktion konkret implementiert ist. Dieses Wissen kann sogar schädlich sein, wenn eine aufrufende Funktion auf Implementierungsdetails aufbaut und bei einer anderen Realisierung möglicherweise nicht mehr läft. Diese Thema führt zum Paradigma der OOP, das hier nur gestreift werden kann. Weitergehende Literatur dazu ist beispielsweise Entwurfsmuster aus dem Addision Wesley Verlag. Syntax einer Funktionsvereinbarung in einer Klasse:

[Private | Friend | Public] [Sub | Function] Funktionsname

Eigenschaften (engl. PROPERTY) werden ähnlich wie Funktionen behandelt, nur das es hier nur die Funktion GET (Wert holen), LET (Wertzuweisung) und GET (Setzen eines Verweises) gibt. Die eigentliche Information, die diese Eigenschaft repräsentieren soll, muss in einer separaten, sinnvollerweisen PRIVATE Variable gespeichert werden. Syntax einer PROPERTY-Vereinbarung in einer Klasse:

[Private | Friend | Public] Property Name [(Argumentliste)] [AS Typ] Anweisungen ... [Name = Rückgabewert] ... [Exit Property] .... Anweisungen .... [Name = Rückgabewert] End Property

Für eine Eigenschaft gibt es die folgenden Funktionsrümpfe:

[Private | Friend | Public] PROPERTY GET name() AS Typ END PROPERTY [Private | Friend | Public] PROPERTY LET name (argument AS Typ) END PROPERTY [Private | Friend | Public] PROPERTY SET name (argument AS Typ) END PROPERTY

Das folgende Beispiel demonstriert den Umgang mit Properties und Methoden. Beispiel: Klasse für komplexe Zahlen
Das Ziel ist es komplexe Zahlen mit Hilfe einer Klasse zu verwalten. Auf diesen ist bekanntlich eine Reihe von Operationen definiert, u.a. sind dies Addition, Multiplikation und Konjugation. Ferner soll sowohl auf den Real- und Imaginärteil, als auch auf Länge zugefriffen werden können. Dabei sollen Implentierungsdetails vollständig verborgen bleiben.
Die Klasse kann wie folgt implementiert werden. Die im Deklarationsabschnit vereinbarten privaten Elemente enthalten die eigentlichen Informationen des Real- und Imaginärteils.

Option Explicit Private RealPart As Double Private ImaginaryPart As Double


Für die Property re (Realteil) wird GET und LET definiert:

Public Property Get re() As Double ' Gibt Realteil zurück re = RealPart End Property Public Property Let re(r As Double) ' Setzt Realteil RealPart = r End Property


Gleiches gilt für den Imaginärteil:

Public Property Get im() As Double ' Gibt Imaginärteil zurück im = ImaginaryPart End Property Public Property Let im(i As Double) ' Setzt Imaginärteil ImaginaryPart = i End Property


Dann definieren wie noch ein Funktion zu schnellen Definition einer komplexen Zahl:

Public Sub Define(re As Double, im As Double) RealPart = re ImaginaryPart = im End Sub


Nun folgenden die Operationen Konjugation, Addition und Multiplikation:

Public Function Conjugation() As ComplexNumber Dim result As ComplexNumber Set result = New ComplexNumber result.re = RealPart result.im = -ImaginaryPart Set Conjugation = result End Function Public Function Add(c As ComplexNumber) As ComplexNumber Dim result As ComplexNumber Set result = New ComplexNumber result.re = c.re + RealPart result.im = c.im + ImaginaryPart Set Add = result End Function Public Function Mult(c As ComplexNumber) As ComplexNumber Dim result As ComplexNumber Set result = New ComplexNumber result.re = c.re * RealPart - c.im * ImaginaryPart result.im = c.re * ImaginaryPart + c.im * RealPart Set Mult = result End Function


Die Funktion length berechnet die Länge der komplexen Zahl:

Public Property Get length() As Double length = Sqr(RealPart ^ 2 + ImaginaryPart ^ 2) End Property


Um eine komplexe Zahl als String darzustellen, gibt es die Funktion AsString:

Public Function AsString() As String ' Gibt die komplexe Zahl als String der Form (re, im) zurück Dim result As String result = "(" + Str(RealPart) & "," & Str(ImaginaryPart) & ")" AsString = result End Function

Auf die privaten Elemente ImaginaryPart und RealPart kann nur in der Klasse zugegriffen werden. Jeder Versuch von außen die Variablen anzusprechen, löst einen Fehler aus. An diesem Beispiel sieht man auch, dass ein Objekt durch die Deklaration allein noch nicht eingerichtet sind, wie dies bei Standarddatentypen der Fall ist. Um ein neues Objekt oder auch Instanz genannt zu erzeugen, muss der NEW Operator angewandt werden.

NEW Klasse

Um einen einen Verweis auf ein Objekt zu setzen, gibt es die SET Anweisung.

SET Objektname = Objekt

Um also ein neues Objekt zu erzeugen und eine Variable darauf verweisen zu lassen, muss man das folgende Statement benutzen:

SET Objektname = NEW Klasse

Nun geben wir noch eine Prozedur an, die diese Klasse anwendet. Es werden zwei komplexe Zahlen c1 und c2 definiert und dann gewisse Operationen aif c1 und c2 durchgeführt, deren Ergebnis jeweils an c3 zugewiesen wird.

Dim c1 As ComplexNumber, c2 As ComplexNumber, c3 As ComplexNumber Set c1 = New ComplexNumber Set c2 = New ComplexNumber c1.Define 2, 0 c2.Define 0, 3 Set c3 = c2.Conjugation MsgBox "Konjugation von " & c2.AsString & " = " & c3.AsString Set c3 = c1.Add(c2) MsgBox "Addition von " & c1.AsString & " und " & _ c2.AsString & " = " & c3.AsString Set c3 = c1.Mult(c2) MsgBox "Addition von " & c1.AsString & " und " & _ c2.AsString & " = " & c3.AsString </span></p> <h3><span style="font-family: Arial;">Polymorphismus

Ein integraler Bestandteil von OOP ist der Polymorphismus, den man am besten mit "Vielgestaltigkeit" übersetzen könnte. Das bedeutet nichts anderes, als dass Klassen gleichnamige Methoden und Eigenschaften besitzen, die ähnliche Aufgaben erfüllen, so dass die Klassen austauschbar sind.
In Visual Basic werden dazu Schnittstellen (engl. Interfaces) definiert, indem eine "abstrakte" Klasse erzeugt wird, die eine gewisse Menge an Elementen besitzt, die aber nicht implementiert werden, sprich leer gelassen werden. (Die Klassen sind nicht wirklich abstrakt, wie man das vielleicht von C++ kennt. Sie können nämlich instanziiert werden. Darum ist auch das Wort abstrakt in Anführungstrichen gesetzt worden.) Eine andere Klasse entspricht dieser Schnittstelle, wenn im Deklarationsabschnitt der Klasse eine IMPLEMENTS Anweisung kommt.
Die Syntax dazu ist:

IMPLEMENTS Klassenname

Die in der Schnittstelle definierten Funktionen müssen dann natürlich auch in dieser Klasse implementiert sein. Eine Standardimplementierung in der Schnittstelle gibt es nicht (was durch die Vererbung in C++ funktionieren würde). Beispiel: Polymorphismus
Um die Bedeutung des Polymorphismuses zu verdeutlichen, soll das nun folgende Beispiel dienen. Es sollen Adressen von Kunden und Lieferanten verwaltet werden.
Kunden haben u.a. die folgenden Informationen : Name, Straße, Ort und Kundennummer. Zu Lieferanten sollen die Daten: Name, Ansprechpartner, Straße und Ort verwaltet werden. Beide Typen sind Adressen, so dass wir eine "abstrakte" Klasse Adresse definieren:

Option Explicit Public Property Get Name() As String End Property Public Property Let Name(s As String) End Property Public Property Get Strasse() As String End Property Public Property Let Strasse(s As String) End Property Public Property Get Ort() As String End Property Public Property Let Ort(s As String) End Property Public Function GetAdress() As String End Function

In dieser Klasse haben wir also die Eigenschaften Name, Strasse und Ort definiert. Mit der Funktion GetAdress soll der Name, die Straße und der Ort jeweils durch Komma getrennt zurückgegeben werden.
Wir haben absichtlich keinen Code für die Routinen eingefügt. Diese müssen wir nun in der Klasse Kundenadresse implementieren. Als weitere Eigenschaft hat eine Kundenadresse eine Kundennummer

Option Explicit Implements Adresse Private mName As String Private mStrasse As String Private mOrt As String Private mKundennummer As String ' ============================================== ' Implementierung ' ============================================== Public Property Let Adresse_Name(s As String) mName = s End Property Public Property Get Adresse_Name() As String Adresse_Name = mName End Property Public Property Let Adresse_Ort(s As String) mOrt = s End Property Public Property Get Adresse_Ort() As String Adresse_Ort = mOrt End Property Public Property Let Adresse_Strasse(s As String) mStrasse = s End Property Public Property Get Adresse_Strasse() As String Adresse_Strasse = mStrasse End Property ' ============================================== ' Implementierung Kundenadresse ' ============================================== Public Function Adresse_GetAdress() As String Dim result As String result = mName & ", " & mStrasse & ", " & mOrt & ", " & mKundennummer Adresse_GetAdress = result End Function ' ============================================== ' Neue Eigenschaft: Kundennummer ' ============================================== Public Property Let Kundennummer(s As String) mKundennummer = s End Property Public Property Get Kundennummer() As String Kundennummer= mKundennummer End Property

Die privaten Datenelemente mName, mStrasse, mOrt und mKundennummer enthalten die eigentlichen Daten (das m steht übrigens für Member). Die Implementierungen haben als Präfix stets den Schnittstellenname "Adresse" getrennt mit einem Unterstrich vom eigentlichen Funktions- oder Eigenschaftsnamen. Also beispielsweise steht "Adresse_Strasse" für die Implementierung der Eigenschaft "Strasse" aus der Schnittstelle "Adresse". (Diese Schreibweise ist vorgeschrieben.)
Die Funktion gibt nicht nur Name, Straße und Ort zurück, sondern auch die Kundennummer.
Eine andere Implementierung ist die Klasse Lieferantenadresse:

Option Explicit Implements Adresse Private mName As String Private mStrasse As String Private mOrt As String Private mAnsprechpartner As String ' ============================================== ' Implementierung ' ============================================== Public Property Let Adresse_Name(s As String) mName = s End Property Public Property Get Adresse_Name() As String Adresse_Name = mName End Property Public Property Let Adresse_Ort(s As String) mOrt = s End Property Public Property Get Adresse_Ort() As String Adresse_Ort = mOrt End Property Public Property Let Adresse_Strasse(s As String) mStrasse = s End Property Public Property Get Adresse_Strasse() As String Adresse_Strasse = mStrasse End Property ' ============================================== ' Implementierung Lieferantenadresse ' ============================================== Public Function Adresse_GetAdress() As String Dim result As String result = mName & ", " & mStrasse & ", " & mOrt & ", " & mAnsprechpartner Adresse_GetAdress = result End Function ' ============================================== ' Neue Eigenschaft: Ansprechpartner ' ============================================== Public Property Let Ansprechpartner(s As String) mAnsprechpartner = s End Property Public Property Get Ansprechpartner() As String Ansprechpartner = mAnsprechpartner End Property

Hier gibt es anstatt der Kundennummer einen Eintrag für den Ansprechpartner. Beachten Sie, dass die Funktion GetAdress ist anders implementiert. Folgender Code verdeutlicht den Umgang mit diesen Klassen:

Dim i As Integer Dim x As Kundenadresse Dim y As Lieferantenadresse Set x = New Kundenadresse Set y = New Lieferantenadresse x.Adresse_Name = "Müller" x.Adresse_Strasse = "Plazerstr. 2" x.Adresse_Ort = "München" x.Kundennummer = "123456" y.Adresse_Name = "Meier" y.Adresse_Strasse = "Ohmstr. 2" y.Adresse_Ort = "München" y.Ansprechpartner = "Hr. Huber" Dim a(2) As Adresse Set a(0) = x Set a(1) = y Set a(2) = New Kundenadresse a(2).Name = "Fischer" a(2).Strasse = "Hundweg 3" a(2).Ort = "Hamburg" For i = 0 To 2 MsgBox a(i).GetAdress Next

Zuerst werden die Variablen x und y einmal als Kunden-, sowie als Lieferantenadresse deklariert und sofort mit neuen Instanzen dieser Klassen besetzt. Danach werden Werte an die jeweiligen Eigenschaften zugewiesen. Dies geschieht bei den von der Klasse Adresse "geerbten" Eigenschaften durch Verwendung des Präfixes Adresse_, also beispielsweise Adresse_Name.
Bei den eigenen Eigenschaften, die nicht aus der implementierten Schnittstelle stammen, wird wie üblich der vereinbarte Bezeichner benutzt. Im Beispiel ist das einmal "Kundennummer" bei der Kundenadresse, sowie "Ansprechpartner" bei der Lieferantenadresse.
Danach wird ein Array a von Adressen deklariert. Wie Sie sehen, wird den Elementen von a sowohl Kunden-, als auch Lieferantenadressen zugewiesen. Es folgen Wertzuweisungen an das Objekt a(2). Hier werden die in der Schnittstelle definierten Bezeichner ohne ein Präfix für die Eigenschaften verwendet! Es ist zu beachten: Für a(2) kann keine Kundennummer angegeben werden, da a(2) per Definition nur als Adresse und nicht als Kundenadresse aufgefasst wird!
Dann werden die Adressen in einer For-Schleife per MessageBox ausgegeben. Dazu wird die Methode "GetAdress" aufgerufen. Und hier kommt der Polymorphismus zum tragen, denn für a(0) und a(2) wird die Implementierung der Kundenadresse, für a(1) die der Lieferantenadresse ausgeführt. Das Ergebnis sieht also folgendermaßen aus:

Müller, Plazerstr. 2, München, 123456 Meier, Ohmstr. 2, München, Hr. Huber Fischer, Hundweg 3, Hamburg,

Zusammenfassung: Zugriff auf Eigenschaften und Methoden bei Polymorphismus
Sei B eine Basisklasse und A eine davon abgeleitete Klasse. Sei weiter b eine Instanz von der Basisklasse B, sowie a eine Instanz von A. Wird auf Elemente der Basisklasse im Objekt b zugegriffen, so werden die in B definierten Bezeichner, also ohne Präfix verwendet. Benutzt man Elemente aus der Basisklasse im Objekt a, der abgeleiteten Instanz, so muss man ein Präfix angeben und zwar "B_".
Für in der abgeleiteten Klasse definierten Elemente wird wie sonst auch üblich kein Präfix verwendet.