3.8 Innere Klassen 

Bisher haben wir uns mit »flachen« Klassen beschäftigt. Unter flach verstehe ich hier eine Klasse, die keine anderen Klassen enthält. In diesem Abschnitt betrachten wir den Fall, dass eine Klasse eine andere enthält. Auch die später im Buch beschriebenen Datentypen können in eine Klasse eingebettet sein und folgen den hier vorgestellten Konzepten. Lediglich der Typ Module kann nicht innerhalb eines anderen Datentyps stehen. Umgekehrt können fast alle Datentypen innere Datentypen enthalten (einzige Ausnahme: siehe Abschnitt 4.3, »Enumerationen«).
Klassen und andere Datentypen werden geschachtelt, um eine logische Beziehung zu der umgebenden Klasse oder einem anderen Typ zu unterstreichen. Es wird keine Verbindung zwischen Objekten geschaffen, lediglich die geschachtelten Datentypen hängen zusammen. So kann zum Beispiel eine Klasse Rad einmal in einer Auto-Klasse und eine andere davon unabhängig in einer Kutsche-Klasse stecken. Beide Räder sind recht unterschiedlich, und es ist zur Unterscheidung sinnvoll, den logischen Bezug zum Fahrzeugtyp herzustellen. Die Deklaration erfolgt analog zu einer ungeschachtelten Klasse. In der folgenden Syntax sind die Zeilenvorschübe zwingend, während optionale Teile in eckige Klammern gesetzt sind. Die Namen Außen und Innen sowie andere kursive Teile dürfen natürlich beliebig umbenannt werden.
[<Modifikatoren>] Class Außen |
Hinweis |
Statt Class können auch andere Datentypen stehen, die innere Typen erlauben. |
Es gibt mehr Modifikatoren als in einer Klasse auf oberster Ebene. Wie Tabelle 3.11 zeigt, sind insbesondere bei der Sichtbarkeit alle für andere Klassenmitglieder erlaubten Spezifikationen möglich und nicht nur Friend und Public.
Art | Beschreibung |
Sichtbarkeit |
Grad der Öffentlichkeit (siehe Abschnitt 3.2, »Kapselung«) |
Redefinition |
Art des Ersatzes oder Zwangs zu einer Definition (siehe Abschnitt 3.13, »Vererbung«) |
Aufteilung |
Definition an verschiedenen Orten (siehe Abschnitt 3.1.11, »Aufteilung der Definition mit Partial«) |
Hinweis |
Innere Datentypen sind implizit an den umgebenden Typ gebunden und weder brauchen noch dürfen sie mit Shared gekennzeichnet werden. |
Die erlaubten Effekte sind dieselben wie für andere Klassen (siehe Tabelle 3.12). Bei anderen Datentypen als Klassen sind nicht immer alle erlaubt.
Art | Beschreibung |
Implementation |
Erfüllung einer Schnittstelle (siehe Abschnitt 3.15, »Schnittstellen: Interface und Implements«) |
Vererbung |
Aufbau auf anderer Klasse (siehe Abschnitt 3.13.1, »Klassenbeziehung durch Inherits«) |
Der Zugriff auf einen inneren Datentyp erfolgt einfach über die Qualifikation mit dem Namen der äußeren Klasse. Beim Zugriff ist formell nicht zu unterscheiden, ob die Klasse Teil einer anderen ist oder in einem gleichlautenden Namensraum steckt (die Klassennamen sind beliebig).
<äußere Klasse>.<innere Klasse> |
Das folgende Codefragment zeigt exemplarisch die Erzeugung einer Variablen vom Typ der inneren Klasse:
Dim var As Außen.Innen = New Außen.Innen()
Schauen wir uns nun das Ganze in einem Beispiel an. Es definiert eine Klasse Auto, in der eine Klasse Motor eingebettet ist.
'...\Klassendesign\InnereKlassen\Prinzip.vb |
Option Explicit On
Namespace Klassendesign
Class Auto
Class Motor
Sub starten()
Console.WriteLine("Motor gestartet!")
End Sub
End Class
End Class
Module Prinzip
Sub Test()
Dim motor As Auto.Motor = New Auto.Motor()
motor.starten()
Console.ReadLine()
End Sub
End Module
End Namespace
Die Ausgabe bestätigt die korrekte Arbeitsweise:
Motor gestartet!
Außer der Objekterzeugung mit einem qualifizierten Namen ist nichts Besonderes an der Verwendung einer inneren Klasse.
3.8.1 Beziehung zur äußeren Klasse 

Die Einbettung einer Klasse in eine andere bezieht sich auch auf den Zugriff auf die sie umgebende Klasse. Die innere Klasse hat vollständigen direkten Zugriff auf alle Mitglieder der äußeren Klasse. Das nächste Codefragment definiert eine Klasse Schüler innerhalb von Schulklasse, die zwei private Felder hat. In der Prozedur Test() wird ein Schüler-Objekt geschaffen, und über die Methoden Auskunft() wird auf die Felder der Klasse Schulklasse zugegriffen. Da die Beziehung der Klassen keine Objektbeziehung darstellt, wird der zweiten Methode ein Schulklasse-Objekt übergeben, das die Objektbeziehung herstellt.
'...\Klassendesign\InnereKlassen\InnenNachAussen.vb |
Option Explicit On
Namespace Klassendesign
Class Schulklasse
Private Shared name As String = "Crey"
Private spitzname As String = "Schnauz"
Class Schüler
Sub Auskunft()
Console.WriteLine("Lehrer: {0}", name)
End Sub
Sub Auskunft(ByVal klasse As Schulklasse)
Console.WriteLine("Genannt: {0}", klasse.spitzname)
End Sub
End Class
End Class
Module Hinein
Sub Test()
Dim pfeiffer As Schulklasse.Schüler = New Schulklasse.Schüler()
pfeiffer.Auskunft()
pfeiffer.Auskunft(New Schulklasse())
Console.ReadLine()
End Sub
End Module
End Namespace
Dass die auszugebenden Felder privat sind, behindert die innere Klasse nicht beim Zugriff.
Lehrer: Crey
Genannt: Schnauz
In der anderen Richtung, das heißt von außen nach innen, bestehen keine Besonderheiten beim Zugriff. Daher wird das folgende Codefragment vom Compiler zurückgewiesen:
Class Schulklasse
Sub Auskunft()
Dim pfeiffer As Schüler = New Schüler()
Console.WriteLine("Pfeiffer: {0}", pfeiffer.zustand) 'Fehler!!
End Sub
Class Schüler
Private zustand As String = "verliebt"
End Class
End Class
Da über den Quelltext keine Objektbeziehung zwischen innerer und äußerer Klasse hergestellt wird, bietet es sich an, diese über den Konstruktor der inneren Klasse zu erzwingen. Das nächste Codefragment erzwingt damit die Beziehung zwischen dem Flugzeug und der Turbine, indem nur ein parametrisierter Konstruktor existiert, der ein Flugzeug-Objekt entgegennimmt.
'...\Klassendesign\InnereKlassen\Objektbeziehung.vb |
Option Explicit On
Namespace Klassendesign
Class Flugzeug
Private name As String
Sub New(ByVal name As String)
Me.name = name
End Sub
Class Turbine
Private flugzeug As Flugzeug
Private nr As Integer
Sub New(ByVal flugzeug As Flugzeug, ByVal nr As Int32)
Me.flugzeug = flugzeug : Me.nr = nr
End Sub
Sub Auskunft()
Console.WriteLine("Turbine {0} in Flugzeug {1}", nr, flugzeug.name)
End Sub
End Class
End Class
Module Beziehung
Sub Test()
Dim flug As Flugzeug = New Flugzeug("A380")
Dim turb As Flugzeug.Turbine
turb = New Flugzeug.Turbine(flug, 3)
turb.Auskunft()
Console.ReadLine()
End Sub
End Module
End Namespace
Die Objektbeziehung kann in der Ausgabe genutzt werden:
Turbine 3 in Flugzeug A380
3.8.2 Sichtbarkeit 

Eine innere Klasse ist bezüglich der Sichtbarkeit ein normales Klassenmitglied. Dies bedeutet, dass sie auch privat sein kann. In Zusammenhang mit dem Geheimnisprinzip bedeutet dies, dass auch kein Objekt vom Typ der inneren Klasse außerhalb der Klasse existieren kann. Daher wird das folgende Codefragment als ungültig zurückgewiesen:
Class Firma
Function Interna() As Struktur 'verboten!!
Return New Struktur
End Function
Private Class Struktur
Partial Private Sub Anlassen()
End Sub
End Class
End Class
3.8.3 Grafikbibliothek: Position des Rechtecks 

In diesem Abschnitt erweitern wir die in Abschnitt 3.1.12, »Grafikbibliothek: Beispiel für Kapitel 3 und 4«, eingeführte und zuletzt in Abschnitt 3.7.9, »Grafikbibliothek: Eigenschaften des Rechtecks«, erweiterte Grafikanwendung. Dem Rechteck wird eine Position hinzugefügt. Sie repräsentiert die linke obere Ecke. Die Motivation, dies in einer inneren Klasse und nicht als Eigenschaft zu machen, sind die Methoden der inneren Klasse zum Bewegen des Rechtecks und zur Ausgabe der absoluten Abmessungen.
'...\Klassendesign\Graphik\InnereKlassen.vb |
Option Explicit On
Namespace Klassendesign
Partial Public Class Rechteck
Class Position
Dim rechteck As Rechteck
Dim x, y As Double
Sub New(ByVal rechteck As Rechteck)
Me.rechteck = rechteck
End Sub
Sub Bewegen(ByVal x As Double, ByVal y As Double)
Me.x = x : Me.y = y
End Sub
Sub Abmessungen()
Console.WriteLine("Rechteck {0}x{1} – {2}x{3}", _
x, y, x + rechteck.a, y + rechteck.b)
End Sub
End Class
End Class
End Namespace
Zuerst wird ein neues Rechteck erzeugt und dem Konstruktor der Position übergeben. Dann wird das Rechteck bewegt und die Abmessungen werden ausgegeben.
'...\Klassendesign\Zeichner\InnereKlassen.vb |
Option Explicit On
Namespace Klassendesign
Partial Class Zeichner
Sub InnereKlassen()
Dim rechteck As New Rechteck(7, 8)
Dim position As New Rechteck.Position(rechteck)
position.Bewegen(100, 200)
position.Abmessungen()
End Sub
End Class
End Namespace
Zur Kontrolle sehen Sie hier die Ausgabe der Methode InnereKlassen():
Rechteck 100x200 – 107x208
Die nächste Erweiterung erfolgt in Abschnitt 3.9.7, »Grafikbibliothek: Vergleich von Rechtecken«.
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.