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

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.15 Schnittstellen: Interface und Implements Zur nächsten ÜberschriftZur vorigen Überschrift

Kommen wir nun zur zweiten Art der Vererbung in Visual Basic. Sie legt nur eine Schnittstelle fest, ist also eine reine Form ohne Inhalt. Aus der Inhaltslosigkeit ergibt sich:


Von einer Schnittstelle können keine Objekte erzeugt werden.


Diese rein formelle Festlegung wird durch einen neuen Datentyp kenntlich gemacht, der Schnittstelle (engl. Interface) genannt wird. Durch sie wird ein Vertrag angeboten, der nur in einer Klasse erfüllt werden kann. Dabei legt die Klasse durch Deklaration fest, dass und wie sie ihn erfüllt.

Eine Schnittstelle ist im Wesentlichen wie eine Klasse aufgebaut, darf aber ausschließlich die in der folgenden Syntax gelisteten Elemente und Modifikatoren enthalten (die letzte Möglichkeit hat eigentlich nichts mit der Idee von Schnittstellen zu tun, siehe Abschnitt 3.8, »Innere Klassen«). Optionale Teile sind in eckige Klammern gesetzt und Alternativen durch einen senkrechten Strich | getrennt. Kursiv gesetzte Teile müssen Sie Ihren Bedürfnissen anpassen. Wie üblich fasst ein Unterstrich zwei Zeilen zu einer logischen Zeile zusammen. Die Typen-Spezifikation wird im Abschnitt 4.4, »Generisches«, erläutert.


[Friend|Public] Interface IName[Typen] 
  [[Shadows|Overloads] Sub name[Typen]([<Parameter>])] 
  [[Shadows|Overloads] Function name[Typen]([<Parameter>]) As Typ] 
  [[[Shadows|Overloads][Default][ReadOnly|WriteOnly] _ 
    Property name([<Indizes>]) As Typ] 
  [[Shadows] Event name As Delegate-Typ] 
  [[[Shadows] Event name([<Parameter>])] 
  [<Typ (zum Beispiel Delegate/Klasse/Schnittstelle)>] 
End Interface

Dabei möchte ich auf folgende Besonderheiten hinweisen:

  • Der Name der Schnittstelle sollte mit dem Buchstaben I beginnen, um sie leichter von Klassen zu unterscheiden und damit einige später zu besprechende Konzepte leichter zu realisieren sind (siehe zum Beispiel Erweiterungsattribute in Abschnitt 4.8, »Attribute«).
  • Schnittstellen enthalten nie Anweisungen oder End-Konstrukte.
  • Schnittstellenmitglieder haben nie Sichtbarkeitsmodifikatoren, sie sind implizit öffentlich.
  • Schnittstellenmitglieder sind nie mit Shared klassengebunden.
  • Schnittstellen enthalten nie Felder oder Operatoren.
  • Schnittstellen dürfen leer sein (Markerschnittstellen).
  • Eine Aufteilung mit Partial ist nicht erlaubt.
  • Typdefinitionen in einer Schnittstelle stellen keinen von einer Klasse zu konkretisierenden Vertrag dar, sondern sind »einfach« geschachtelte Typen (siehe Abschnitt 3.8, »Innere Klassen«).

Einige dieser Beschränkungen sind notwendigerweise mit dem Konzept einer Schnittstelle verbunden, wie zum Beispiel das Fehlen von Anweisungen. Andere sind einfach so in der Sprachdefinition von Visual Basic festgelegt.

Kommen wir nun dazu, wie eine Klasse von einer Schnittstelle Gebrauch macht. Dazu deklariert sie mittels Implements, dass die Klasse die Schnittstelle konkretisiert. Außerdem wird an jedem Klassenmitglied vermerkt, wenn es ein Schnittstellenmitglied implementiert. In der folgenden Syntax sind optionale Teile in eckige Klammern gesetzt und kursiv gesetzte Teile müssen Sie Ihren Bedürfnissen anpassen. Wie üblich fasst ein Unterstrich zwei Zeilen zu einer logischen Zeile zusammen. Die Typen-Spezifikation wird im Abschnitt 4.4, »Generisches«, erläutert.


Class Klasse 
  Implements IName 
  [<Modifikatoren>] Sub Name[Typen]([<Parameter>]) Implements IName.Name[, ...] 
    [<Anweisungen>] 
  End Sub 
  [<Modifikatoren>] Function Name[Typen]([<Parameter>]) As Typ _ 
  Implements IName.Name[, ...] 
    [<Anweisungen>] 
  End Function 
  [<Modifikatoren>][Default][ReadOnly|WriteOnly] _ 
  Property Name ([<Indizes>]) As Typ Implements IName.Name[, ...] 
    [<Sichtbarkeit>] Get 
      [<Anweisungen>] 
    End Get 
    [<Sichtbarkeit>] Set(wert As Typ) 
      [<Anweisungen>] 
    End Set 
  End Property 
  [<Sichtbarkeit>][Shadows] Event name As Delegate-Typ _ 
    Implements IName.Name[, ...] 
  [<Sichtbarkeit>][Shadows] Event name([<Parameter>]) _ 
    Implements IName.Name[, ...] 
End Class

Als Modifikatoren sind weniger erlaubt als bei allgemeinen Methoden und Eigenschaften (siehe Tabelle 3.26).


Tabelle 3.26 Modifikatoren eines implementierenden Klassenmitglieds

Art Beschreibung

Sichtbarkeit

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

Redefinition

Art des Ersatzes oder Zwangs zu einer Definition (siehe Abschnitt 3.13.4, »Modifikation: Shadows und Overloads (Overrides)«, Abschnitt 3.13.5, »Abstrakte Klassen: MustInherit und MustOverride«, Abschnitt 3.14.1, »Virtuelle Methoden: Overridable und Overrides« und Abschnitt 3.3.4, »Überladung«) ohne: NotOverridable.


Die Implementation einer Schnittstelle erweitert den Datentyp eines Objekts:


Ein Objekt einer Klasse, die eine Schnittstelle implementiert, ist (unter anderem) vom Typ der implementierten Schnittstelle.


Auch bei der Implementation einer Schnittstelle gibt es ein paar Besonderheiten:

  • Eine Klasse darf, im Gegensatz zur Vererbung, mehrere Schnittstellen implementieren.
  • Ein Klassenmitglied kann beliebig viele Schnittstellenmitglieder zugleich implementieren.
  • Es müssen alle Schnittstellenmitglieder implementiert werden (gegebenenfalls über Vererbung).
  • Dasselbe Schnittstellenmitglied darf nicht mehrfach implementiert werden.
  • Implementierende Klassenmitglieder sind nie mit Shared klassengebunden.
  • Externe Funktionen können nie eine Schnittstelle implementieren.
  • Namen von Schnittstellenmitgliedern und implementierenden Klassenmitgliedern dürfen unterschiedlich sein.
  • Parametertypen in der Schnittstelle und der implementierenden Klasse sind identisch.
  • Öffentliche Schnittstellenmitglieder können von privaten Klassenmitgliedern implementiert werden (meiner Meinung nach ein unglückliches Sprachdesign).

Als Beispiel dient der Vertrag der Bürger eines Staates mit den Finanzbehörden. Da alle Steuern zahlen müssen, aber nicht jeder in derselben Art und Weise, bietet es sich an, die Verpflichtung, dass zu zahlen ist, in einer Schnittstelle IBürger zu deklarieren und erst in den implementierenden Klassen festzulegen, wie bezahlt wird. Dazu deklarieren die Klassen Bauer und Händler mit Implements IBürger, dass sie die Schnittstelle implementieren. Zusätzlich wird die implementierende Methode mit IBürger.SteuernZahlen markiert. In einer Klasse, die mehrere passende Methodensignaturen bereitstellt, ist dies logisch notwendig und wird von Visual Basic zwecks Konsistenz immer gefordert. Die verschiedenen Implementierungen reflektieren die unterschiedlichen Arten, Steuern zu zahlen. Die Methode Test() schließlich greift auf die verschiedenen Steuerzahler über den Typ der Schnittstelle zu.


Hinweis
Um die Beispiele einfach zu halten, enthalten sie nur je ein Schnittstellenmitglied. Im Allgemeinen kann eine Schnittstelle viele Mitglieder haben, inklusive der überladenen.



'...\Klassendesign\Schnittstellen\Prinzip.vb

Option Strict On 
Namespace Klassendesign

  Interface IBürger 
    Sub SteuernZahlen() 
  End Interface

  Class Bauer : Implements IBürger 
    Sub Steuern() Implements IBürger.SteuernZahlen 
      Console.WriteLine("Bauer zahlt in Naturalien.") 
    End Sub 
  End Class

  Class Händler : Implements IBürger 
    Sub SteuernZahlen() Implements IBürger.SteuernZahlen 
      Console.WriteLine("Händler zahlt mit Geld.") 
    End Sub 
  End Class

  Module Prinzip 
    Sub Test() 
      For Each z As IBürger In New IBürger() {New Händler(), New Bauer()} 
        z.SteuernZahlen() 
      Next 
      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Die Ausgabe zeigt, wie Händler und Bauer, als IBürger angesprochen, Steuern zahlen:

Händler zahlt mit Geld. 
Bauer zahlt in Naturalien.

Hinweis
.NET arbeitet oft mit Schnittstellen, wie zum Beispiel IEnumerable und IDisposable, aber leider nicht ganz konsequent. Zum Beispiel setzt die For Each-Schleife nicht unbedingt IEnumerable voraus, sondern es reicht eine zugängliche Methode der Signatur Function GetEnumerator() As Collections.IEnumerator.



Rheinwerk Computing - Zum Seitenanfang

3.15.1 Benennungen und Parameter Zur nächsten ÜberschriftZur vorigen Überschrift

Die Namen der implementierenden Klassenmitglieder werden meist so gewählt wie die der Schnittstellenmitglieder. Dies ist aber nicht zwingend und sehr nützlich, wenn dasselbe Klassenmitglied gleichzeitig verschiedennamige Mitglieder mehrerer Schnittstellen implementieren soll. Das folgende Codefragment zeigt die zwei Schnittstellen ITransporter und IAuto, die für die Eigenschaft der maximalen Zuladung die beiden verschiedenen Namen Nutzlast() und Beladung() wählen. Da dasselbe Fahrzeug dieselbe Zuladung hat, egal ob Sie es als Transporter oder Auto ansehen, werden beide Schnittstellenmethoden in der Klasse LKW in einer einzigen Eigenschaft Zuladung() implementiert. Die Methode Test() zeigt, dass der Typ einer Variablen den Namen der Eigenschaft bestimmt.


'...\Klassendesign\Schnittstellen\Benennungen.vb

Option Strict On 
Namespace Klassendesign

  Interface ITransporter 
    ReadOnly Property Nutzlast() As Integer 
  End Interface

  Interface IAuto 
    ReadOnly Property Beladung() As Integer 
  End Interface

  Class LKW : Implements ITransporter, IAuto 
    Public ReadOnly Property Zuladung() As Integer _ 
    Implements IAuto.Beladung, ITransporter.Nutzlast 
      Get 
        Return 1000 
      End Get 
    End Property 
  End Class

  Module Benennungen 
    Sub Test() 
      Dim lkw As LKW = New LKW() : Dim trans As ITransporter = lkw 
      Console.WriteLine("Zuladung {0}", lkw.Zuladung) 
      Console.WriteLine("Zuladung {0}", trans.Nutzlast) 
      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Beide Variablen greifen auf dasselbe Objekt zu und liefern dieselbe Last, egal wie sie genannt wird.

Zuladung 1000 
Zuladung 1000

Bei der Implementierung mehrerer Ereignisse in demselben Ereignis gibt es noch eine kleine Stolperfalle. Dies ist nur möglich, wenn die Ereignisse identisch dasselbe Delegate verwenden. Da ein Ereignis ohne As-Klausel implizit ein neues Delegate definiert, können also nur solche Ereignisse in einem einzigen Ereignis implementiert werden, die mit einer As-Klausel dasselbe Delegate spezifizieren. Das folgende Codefragment zeigt zwei Schnittstellen, IAusbilder und ITrainer, mit einem Ereignis, das dessen Typ über ein Delegate festlegt und eine Schnittstelle IChorleiter, die den Ereignistyp implizit definiert. Die beiden ersten Ereignisse werden in der Klasse Lehrer in einem einzigen Ereignis implementiert. Dies ist beim Ereignis der dritten Schnittstelle nicht möglich, und es wird in einem zusätzlichen Ereignis implementiert.


'...\Klassendesign\Schnittstellen\Ereignisse.vb

Option Strict On 
Namespace Klassendesign

  Delegate Sub Aufruf()

  Interface IAusbilder 
    Event Anwesend As Aufruf 
  End Interface

  Interface ITrainer 
    Event Zählen As Aufruf 
  End Interface

  Interface IChorleiter 
    Event Stimmen() 
  End Interface

  Class Lehrer : Implements IAusbilder, ITrainer, IChorleiter

    Event Appell As Aufruf Implements IAusbilder.Anwesend, ITrainer.Zählen

    Event Appell2() Implements IChorleiter.Stimmen 
    ... 
  End Class 
  ... 
End Namespace

Keine Wahl besteht hingegen bei den Typen. So kann zum Beispiel aus einer Eigenschaft einer Schnittstelle nicht eine implementierende Methode in einer Klasse werden. Auch müssen die Typen von Parametern erhalten bleiben, inklusive der Standardwerte optionaler Parameter. Das nächste Beispiel berechnet den Einkommenssteuersatz in Abhängigkeit von den optionalen Einnahmen. Der Standardwert ist so gewählt, dass ohne Einkommensangabe der Höchststeuersatz gilt. Die implementierende Klasse Angestellter muss diesen Standardwert übernehmen. Jeder andere Wert verursacht einen Compilerfehler.


'...\Klassendesign\Schnittstellen\Parameter.vb

Option Strict On 
Namespace Klassendesign

  Interface ISteuerzahler 
    Function Steuersatz(Optional ByVal einkommen As Int32 = 250000) As Int32 
  End Interface

  Class Angestellter : Implements ISteuerzahler 
    Function Steuersatz(Optional ByVal einkommen As Int32 = 250000) _ 
    As Int32 Implements ISteuerzahler.Steuersatz 
      If einkommen < 7664 Then Return 0 
      Dim d As Integer = 12740 – 7664 
      If einkommen < 12740 Then Return 15 + (einkommen – 7664) / d * 9 
      d = 52152 – 12740 
      If einkommen < 52152 Then Return 24 + (einkommen – 12740) / d * 18 
      If einkommen < 250000 Then Return 42 
      Return 45 
    End Function 
  End Class

  Module Parameter 
    Sub Test() 
      Dim an As Angestellter = New Angestellter() 
      For Each ein As Integer In New Integer() {7000, 10000, 20000, 40000} 
        Console.WriteLine("{1}% Steuern bei {0}", ein, an.Steuersatz(ein)) 
      Next 
      Console.WriteLine("{0}% Steuern ohne Angabe", an.Steuersatz()) 
      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Die Ausgabe bestätigt die korrekte Arbeitsweise:

0% Steuern bei 7000 
19% Steuern bei 10000 
27% Steuern bei 20000 
36% Steuern bei 40000 
45% Steuern ohne Angabe

Hinweis
Die Parametertypen (inklusive ByVal/ByRef) und gegebenenfalls Standardwerte in der Schnittstelle und der implementierenden Klasse müssen identisch sein. Selbst implizite (erweiternde) Typumwandlungen werden nicht berücksichtigt.



Rheinwerk Computing - Zum Seitenanfang

3.15.2 Schnittstellen und Vererbung Zur nächsten ÜberschriftZur vorigen Überschrift

Geerbte Schnittstellenimplementierungen

Ist eine Schnittstelle einmal implementiert, vererbt sich diese Tatsache auch auf alle Kindklassen, so als sei die Schnittstelle in der Kindklasse implementiert worden. Das folgende Beispiel verwendet die folgende Vererbungshierarchie:

IHaus 
  +-Haus 
      +-Bude 
      +-Villa

Die Schnittstelle IHaus hat zwei Methoden, die in der Klasse Haus implementiert werden. Dann wird von Haus die Klasse Bude abgeleitet, ohne Haus zu ändern oder zu ergänzen (die Angabe von Implements IHaus ist optional). Dagegen wird in der Klasse Villa eine der beiden Methoden neu implementiert. Dies ersetzt nur diese eine Signatur, ohne die andere zu beeinflussen. In der Methode Test() werden eine Villa und eine Bude über die Basisklassenreferenz Haus genutzt.


'...\Klassendesign\Schnittstellen\Vererbung.vb

Option Strict On 
Namespace Klassendesign

  Interface IHaus 
    Overloads Sub Wand(ByVal material As String) 
    Overloads Sub Wand(ByVal kostenrahmen As Double) 
  End Interface

  Class Haus : Implements IHaus 
    Overridable Sub Wand(ByVal material As String) Implements IHaus.Wand 
      Console.WriteLine("Wanddicke Haus " & If(material = "Beton", 20, 25)) 
    End Sub

    Overridable Sub Wand(ByVal kostenrahmen As Double) Implements IHaus.Wand 
      Console.Write("Materialwahl Haus : ") 
      Wand(If(kostenrahmen <= 15, "Beton", "Stein")) 
    End Sub 
  End Class

  Class Bude : Inherits Haus : End Class

  Class Villa : Inherits Haus : Implements IHaus 
    Overrides Sub Wand(ByVal kostenrahmen As Double) Implements IHaus.Wand 
      Console.Write("Materialwahl Villa: ") 
      Wand(If(kostenrahmen <= 10, "Beton", "Stein")) 
    End Sub 
  End Class

  Module Vererbung 
    Sub Test() 
      Dim bude As IHaus = New Bude() : bude.Wand(15) 
      Dim villa As IHaus = New Villa() : villa.Wand(15) 
      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Der erste Aufruf von Wand() nutzt die Implementation der Klasse Haus. Die zweite Ausgabe zeigt, dass die neue Implementation von Villa verwendet wird.

Materialwahl Haus : Wanddicke Haus 20 
Materialwahl Villa: Wanddicke Haus 25

Hinweis
Ist eine Schnittstelle implementiert, dürfen alle Kindklassen mit Implements diese Tatsache erneut deklarieren. Dabei kann ein Teil oder die ganze Schnittstelle neu implementiert werden.


Basistyp

Die Aussage, dass alle Klassen als Wurzel die Klasse Object haben, erstreckt sich nicht auf Schnittstellen. Das nächste Codefragment testet auf den Elterntyp einer Schnittstelle:


'...\Klassendesign\Schnittstellen\Wurzel.vb

Option Strict On 
Namespace Klassendesign

  Interface IWurzel : End Interface

  Module Wurzel 
    Sub Test() 
      If GetType(IWurzel).BaseType Is Nothing Then _ 
        Console.WriteLine("Object ist nicht Basis!") 
      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Wie die Ausgabe zeigt, hat die Schnittstelle nicht Object als Basis:

Object ist nicht Basis!

Trotzdem können Schnittstellenreferenzen auf Mitglieder der Klasse Object zugreifen und Erweiterungsmethoden vom Typ Object nutzen (siehe Abschnitt 4.8.4, »Klassenerweiterungen: Extension«), da eine implizite Konvertierung existiert. Dazu zeigt das nächste Beispiel den Zugriff auf GetType() der Klasse Object:


'...\Klassendesign\Schnittstellen\Object.vb

Option Strict On 
Namespace Klassendesign

  Interface IObject : End Interface

  Class Objekt : Implements IObject : End Class

  Module KonvertierungZuObject 
    Sub Test() 
      Dim ref As IObject = New Objekt() 
      Console.WriteLine("Typ: {0}", ref.GetType().Name) 
      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Der korrekte Typ wird ausgegeben:

Typ: Objekt

Rheinwerk Computing - Zum Seitenanfang

3.15.3 Schnittstellenvererbung Zur nächsten ÜberschriftZur vorigen Überschrift

Neben Klassen können auch Schnittstellen voneinander erben. Im Gegensatz zu Klassen gilt:


Eine Schnittstelle darf mehrere Elternschnittstellen haben, aber keine Elternklasse.


Dabei werden alle Mitglieder aller Elternklassen übernommen, sodass bei der Implementation der Kindschnittstelle auch alle Mitglieder aller Elternklassen berücksichtigt werden müssen. Sonst wäre ja die Kindschnittstelle nicht vollständig implementiert. Im folgenden Beispiel erbt die Schnittstelle IZitrusfrucht von den Schnittstellen IObst und ILebensmittel. Die Klasse Orange deklariert Implements IZitrusfrucht und muss daher alle drei Methoden der Schnittstelle implementieren, wobei die Methoden Vitamine() und Kalorien() über Vererbung in die Schnittstelle gelangt sind.


'...\Klassendesign\Schnittstellen\Schnittstellenvererbung.vb

Option Strict On 
Namespace Klassendesign

  Interface IObst 
    Sub Vitamine() 
  End Interface

  Interface ILebensmittel 
    Sub Kalorien() 
  End Interface

  Interface IZitrusfrucht : Inherits IObst, ILebensmittel 
    Sub Schälen() 
  End Interface

  Class Orange : Implements IZitrusfrucht

    Sub Vitamine() Implements IObst.Vitamine 
      Console.WriteLine("Orange hat Vitamin C.") 
    End Sub

    Sub Schälen() Implements IZitrusfrucht.Schälen 
      Console.WriteLine("Orange wird geschält mit einem Messer.") 
    End Sub

    Sub Kalorien() Implements ILebensmittel.Kalorien 
      Console.WriteLine("Orange hat weniger Kalorien als Schokolade.") 
    End Sub

  End Class

  Module Schnittstellenvererbung 
    Sub Test() 
      Dim orange As IZitrusfrucht = New Orange() 
      orange.Vitamine() : orange.Kalorien() : orange.Schälen() 
      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Über die Schnittstellenreferenz sind alle drei implementierten Methoden erreichbar:

Orange hat Vitamin C. 
Orange hat weniger Kalorien als Schokolade. 
Orange wird geschält mit einem Messer.

Hinweis
Wie in Abschnitt 3.13.2, »Sichtbarkeitsmodifikatoren«, beschrieben wird, darf eine Kindschnittstelle nicht sichtbarer sein als ihre Eltern.


Mehrfachvererbung

Bei der Vererbung von Schnittstellen werden nur Signaturen vererbt. Wird nun durch Mehrfachvererbung auf verschiedenen Wegen dieselbe Signatur vererbt, ist sie immer noch identisch dieselbe und findet sich in der Kindschnittstelle auch nur einmalig wieder. Das folgende Beispiel stellt die Methode Schneiden() in ITaschenmesser durch Vererbung sowohl über IMesser als auch über ISäge zur Verfügung. Dennoch reicht es, in der Klasse Taschenmesser die Methode nur einmalig zu implementieren. Statt Implements ITaschenmesser.Schneiden darf daher auch Implements IMesser.Schneiden oder Implements ISäge.Schneiden verwendet werden. Da alle drei Möglichkeiten dieselbe Methode implementieren, darf auch nur eine der drei vorkommen.


'...\Klassendesign\Schnittstellen\Mehrfachvererbung.vb

Option Strict On 
Namespace Klassendesign

  Interface ISchnittwerkzeug : Sub Schneiden() : End Interface

  Interface IMesser : Inherits ISchnittwerkzeug : End Interface 
  Interface ISäge : Inherits ISchnittwerkzeug : End Interface

  Interface ITaschenmesser : Inherits IMesser, ISäge : End Interface

  Class Taschenmesser : Implements ITaschenmesser 
    Sub Schneiden() Implements ITaschenmesser.Schneiden 
      Console.WriteLine("Mit Taschenmesser Schneiden") 
    End Sub 
  End Class

  Module Mehrfachvererbung 
    Sub Test() 
      Dim taschenmesser As ITaschenmesser = New Taschenmesser() 
      taschenmesser.Schneiden() 
      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Wenn eine Schnittstelle mehrere Elternschnittstellen haben kann, tauchen Probleme auf, die bei der Einfachvererbung der Klassen nicht auftreten können. Wenn eine Schnittstelle identische Signaturen aus verschiedenen Elternschnittstellen übernimmt, so hat sie zwei (mehrdeutige) Mitglieder, die gleich lauten. Beim Zugriff würde ein Konflikt entstehen, da nicht entschieden werden kann, welche denn nun gemeint ist. Daher muss über die passende Basisschnittstellenreferenz zugegriffen werden. Das folgende Codefragment definiert eine Schnittstelle IStereoanlage, die sowohl von IRadio als auch von ICompactDisc die Methodensignatur Musik() erbt. Die Klasse Musikanlage implementiert die gleichlautenden Signaturen in verschiedenen Methoden. In der Realität ist der Begriff von Musik ja schließlich auch recht unterschiedlich. In der Methode Test() kommt es nun zum Zugriff auf die Methode Musik() über die mehrdeutige Schnittstellenreferenz. Der direkte Zugriffsversuch wird vom Compiler zurückgewiesen und ist daher auskommentiert. Erst durch eine Typumwandlung, die eindeutig klarmacht, auf welche Methode zugegriffen werden soll, können die beiden Methoden verwendet werden.


'...\Klassendesign\Schnittstellen\Mehrdeutig.vb

Option Strict On 
Namespace Klassendesign

  Interface IRadio : Sub Musik() : End Interface

  Interface ICompactDisc : Sub Musik() : End Interface

  Interface IStereoanlage : Inherits IRadio, ICompactDisc : End Interface

  Class Musikanlage : Implements IStereoanlage 
    Sub Hifi() Implements ICompactDisc.Musik 
      Console.WriteLine("'s Brent, briderlech, 's brent ...") 
    End Sub

    Sub Schepper() Implements IRadio.Musik 
      Console.WriteLine("All we like sheep ...") 
    End Sub 
  End Class

  Module Mehrdeutig 
    Sub Test() 
      Dim anlage As IStereoanlage = New Musikanlage() 
      'anlage.Musik()                          'Compilerfehler: mehrdeutig!! 
      CType(anlage, IRadio).Musik() 
      CType(anlage, ICompactDisc).Musik() 
      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Die verschiedenen Arten der Musik machen sich in der Ausgabe bemerkbar:

All we like sheep ... 
's Brent, briderlech, 's brent ...

Rheinwerk Computing - Zum Seitenanfang

3.15.4 Schnittstelle oder abstrakte Klasse? Zur nächsten ÜberschriftZur vorigen Überschrift

Die Konzepte einer Schnittstelle und abstrakten Klasse sind sehr ähnlich. Es gibt keine allgemeine Empfehlung, wann welche benutzt werden sollte. Die Tabelle 3.27 soll eine Hilfestellung für die Entscheidung sein. Ist keine eindeutige Entscheidung möglich, wählen Sie eine Möglichkeit und experimentieren damit. In der Programmierpraxis werden Sie dann »Ihren« Weg finden.


Tabelle 3.27 Schnittstelle versus abstrakte Klasse

Art Schnittstelle abstrakte Klasse

Vererbung

mehrfach

einfach

Implementation

nie, reiner Vertrag

darf (oft allgemeine Funktionalität)

Abstraktion

vollständig

von gar nicht bis vollständig

Änderungen nach Freigabe

unbedingt vermeiden

Signaturen und Rückgabetypen unbedingt unverändert, im implementierten Teil kann notfalls geändert werden.

typisches Einsatzgebiet

Toolklassen, zum Beispiel zum Sortieren und Selektieren

an sich konkrete Klassen, in denen ein Teil erst in Kindklassen festgelegt wird, zum Beispiel Drucker mit allgemeiner Warteschlange



Rheinwerk Computing - Zum Seitenanfang

3.15.5 Grafikbibliothek: Flächen 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 zuletzt in Abschnitt 3.14.7, »Grafikbibliothek: Objektsammlungen«, erweiterte Grafikanwendung. Dazu werden zwei Schnittstellen eingeführt, die den Namen und den Umfang eines Umrisses beschreiben:


'...\Klassendesign\Graphik\Schnittstellen.vb

Option Explicit On 
Namespace Klassendesign

  Public Interface IFigur 
    Function Name() As String 
  End Interface

  Public Interface IUmriss : Inherits IFigur 
    ReadOnly Property Umfang() As Double 
  End Interface 
  ... 
End Namespace

Im Rechteck werden die Schnittstellen implementiert. Der Name soll polymorph sein und wird mit Overridable gekennzeichnet sowie im Quadrat überschrieben.


'...\Klassendesign\Graphik\Schnittstellen.vb

Option Explicit On 
Namespace Klassendesign 
  ... 
  Partial Public Class Rechteck : Inherits Vieleck : Implements IUmriss 
    Public Overridable Function Name() As String Implements IFigur.Name 
      Return "Rechteck" 
    End Function

    Public ReadOnly Property Umfang() As Double Implements IUmriss.Umfang 
      Get 
        Return 2 * a + 2 * b 
      End Get 
    End Property 
  End Class

  Partial Public NotInheritable Class Quadrat : Inherits Rechteck 
    Public Overrides Function Name() As String 
      Return "Quadrat" 
    End Function 
  End Class

End Namespace

Zum Test werden ein paar Umrisse erzeugt und über den Schnittstellentyp angesprochen:


'...\Klassendesign\Zeichner\Scnittstellen.vb

Option Explicit On 
Namespace Klassendesign 
  Partial Class Zeichner 
    Sub Schnittstellen() 
      Dim umrisse() As IUmriss = {New Quadrat(7), New Rechteck(4, 6)} 
      For Each u As IUmriss In umrisse 
        Console.WriteLine(u.Name() & " mit " & u.Umfang & " Umfang") 
      Next 
    End Sub 
  End Class 
End Namespace

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

Quadrat mit 28 Umfang 
Rechteck mit 20 Umfang

Die nächste Erweiterung erfolgt in Abschnitt 3.2.6, »Grafikbibliothek: private Größe des Rechtecks«.



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen. >> Zum Feedback-Formular
<< zurück
  Zum Katalog
Zum Katalog: Visual Basic 2008
Visual Basic 2008
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Katalog: Visual Basic 2012






 Visual Basic 2012


Zum Katalog: Schrödinger programmiert C++






 Schrödinger
 programmiert C++


Zum Katalog: IT-Handbuch für Fachinformatiker






 IT-Handbuch für
 Fachinformatiker


Zum Katalog: Professionell entwickeln mit Visual C# 2012






 Professionell
 entwickeln mit
 Visual C# 2012


Zum Katalog: Windows Presentation Foundation






 Windows Presentation
 Foundation


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
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.


[Rheinwerk Computing]

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