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.14 Polymorphie Zur nächsten ÜberschriftZur vorigen Überschrift

Das Wort Polymorphie bedeutet Vielgestaltigkeit. In Visual Basic bezieht sich das auf das Verhalten und die Eigenschaften eines Objekts, wenn es verwendet wird und dann nicht gemäß der Verwendung, sondern seines wahren Typs reagiert. Wenn Sie zum Beispiel in einem Restaurant ein Essen bestellen, wird es der Koch gemäß seinen Fähigkeiten zubereiten, obwohl Sie keine konkreten Anweisungen dazu gegeben haben. So kann ohne Ihr Zutun aus der einfachen Bestellung »Fischsuppe« eine raffinierte »Bouillabaisse« werden. Als Gast sind Sie nicht primär daran interessiert, wie das gute Essen gemacht wird. Sie möchten bestellen und gut essen. Analog verhält es sich bei Software. Eine Bildbearbeitung zum Beispiel verwendet den immer gleichen Befehl zum Ausdrucken eines Fotos. Dabei soll die Qualität des Ausdrucks immer so gut sein, wie es mit der verwendeten Hardware geht. Damit nicht bei jedem neuen Drucker die Software angepasst werden muss, wird mit einer Referenz auf einen »allgemeinen« Drucker gearbeitet, die auf den gerade aktiven Drucker verweist. So kann während der Laufzeit der Drucker gewechselt werden, ohne Abstriche in der »optimalen« Qualität machen zu müssen.

Fangen wir mit einem einfachen Beispiel an. Das folgende Codefragment definiert einen allgemeinen Drucker mit einer Methode, die eine Überschrift druckt. Davon ist ein Textdrucker abgeleitet, der die Überschrift gesperrt ausgibt. In der Methode Test() wird der Druckbefehl ÜberschriftDrucken() eines Textdruckers über eine Referenz der Basisklasse Drucker ausgeführt.


'...\Klassendesign\Polymorphie\NichtPolymorph.vb

Option Strict On 
Namespace Klassendesign

  Class Drucker 
    Sub ÜberschriftDrucken(ByVal text As String) 
      Console.Write(text) 
    End Sub 
  End Class

  Class Textdrucker : Inherits Drucker 
    Sub ÜberschriftDrucken(ByVal text As String) 
      For Each ch As Char In text : Console.Write(ch & " ") : Next 
    End Sub 
  End Class

  Module NichtPolymorph 
    Sub Test() 
      Dim dr As Drucker = New Textdrucker() 
      dr.ÜberschriftDrucken("Überschrift") 
      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Die Ausgabe zeigt, dass die Druckmethode der Basisklasse angesprochen wird, da der Text nicht gesperrt (ohne Leerzeichen) erscheint.

Überschrift

Hinweis
Es gibt viele Programmierer, die nichtpolymorphe Methoden als ungeeignet für die Vererbung ansehen (Java zum Beispiel bindet fast alle Methoden polymorph).



Rheinwerk Computing - Zum Seitenanfang

3.14.1 Virtuelle Methoden: Overridable und Overrides Zur nächsten ÜberschriftZur vorigen Überschrift

Um die Polymorphie eines Klassenmitglieds zu ermöglichen, muss es mit dem Modifikator Overridable gekennzeichnet werden und, wenn gewünscht, in einer Kindklasse mit dem Modifikator Overrides neu definiert werden. Dadurch legt der Compiler die sogenannte virtuelle Methodentabelle an, die für jedes Objekt das Klassenmitglied auf die Implementierung umlenkt, die dem Typ des Objekts am nächsten liegt.

Die folgende Syntax zeigt alle erlaubten polymorphen Klassenmitglieder. Alle frei wählbaren Bezeichner sind kursiv gesetzt, und Typ und Ret stehen für einen beliebigen Datentyp. Das Zeichen <...> ist ein Platzhalter für einen Zeilenvorschub, optionale Anweisungen und ein korrespondierendes End-Konstrukt. Bis auf den Parameter der Set-Routine sind die Anzahl und Typen der Parameter frei, aber eine Eigenschaft mit Default muss mindestens einen Parameter haben. Optionale Teile sind in eckige Klammern gesetzt, kursive Teile müssen Sie Ihren Bedürfnissen anpassen, und der Unterstrich verbindet wie üblich Teile einer einzelnen logischen Zeile.


Class Virtuell 
  [<Modifikatoren>] Overridable Sub Prozedur([Parameter]) <...> 
  [<Modifikatoren>] Overridable Function Funktion([Parameter]) As Typ <...> 
  [<Modifikatoren>] Overridable [Default] _ 
  Property Eigenschaft([Parameter]) As Ret 
    Get <...> 
    Set(wert As Ret) <...> 
  End Property 
  [<Modifikatoren>] Overridable [Default] ReadOnly _ 
  Property Eigenschaft([Parameter]) As Ret 
    Get <...> 
  End Property 
  [<Modifikatoren>] Overridable [Default] WriteOnly _ 
  Property Eigenschaft([Parameter]) As Ret 
    Set(wert As Ret) <...> 
  End Property 
End Class

Die Auswahl an Modifikatoren ist recht beschränkt (siehe Tabelle 3.25).


Tabelle 3.25 Modifikatoren eines polymorphen Klassenmitglieds

Art Beschreibung

Sichtbarkeit

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

Redefinition

Verdecken mit Shadows oder ergänzen mit Overloads (siehe Abschnitt 3.13.4, »Modifikation: Shadows und Overloads (Overrides)«) sowie Zwang zur Definition mit MustOverride (siehe Abschnitt 3.13.5, »Abstrakte Klassen: MustInherit und MustOverride«)


Dabei ist Folgendes zu beachten:

  • Felder, Ereignisse, Delegaten, Typen, Operatoren und Konstruktoren sind nie polymorph.
  • Polymorphe Klassenmitglieder sind nie mit Shared klassengebunden.
  • Es gibt keine polymorphen externen Funktionen.
  • Die Sichtbarkeit von Overridable-Mitgliedern muss in der Eltern- und der Kindklasse identisch sein. (Ausnahme: Protected Friend wird in einer anderen Anwendung zu Protected.)
  • Private Overridable und Overridable NotOverridable sind verboten,
  • MustOverride Overrides ist erlaubt, wenn die Elternklasse ein passendes Mitglied hat.

Damit lässt sich das Druckerbeispiel so formulieren, dass der Druckertyp auch in einer Basisklassenreferenz berücksichtigt wird:


'...\Klassendesign\Polymorphie\Polymorph.vb

Option Strict On 
Namespace Klassendesign

  Class FlexiblerDrucker 
    Overridable Sub ÜberschriftDrucken(ByVal text As String) 
      Console.WriteLine(text) 
    End Sub 
  End Class

  Class Laserdrucker : Inherits FlexiblerDrucker 
    Overrides Sub ÜberschriftDrucken(ByVal text As String) 
      For Each ch As Char In text : Console.Write(ch & " ") : Next 
      Console.WriteLine() 
    End Sub 
  End Class

  Module Polymorph 
    Sub Test() 
      Dim dr As FlexiblerDrucker = New Laserdrucker() 
      dr.ÜberschriftDrucken("Überschrift") 
      dr = New FlexiblerDrucker() 
      dr.ÜberschriftDrucken("Überschrift") 
      Console.ReadLine() 
    End Sub 
    ... 
  End Module 
End Namespace

Nun erfolgt die Ausgabe so gut, wie es der gerade aktuelle Drucker kann, obwohl dieselbe Referenzvariable verwendet wird.

Ü b e r s c h r i f t 
Überschrift

Damit der Objekttyp auch bei Basisklassenreferenzen berücksichtigt werden kann, gilt:


Durch Overridable wird eine Methode oder Eigenschaft erst zur Laufzeit dynamisch an ein Objekt gebunden. Der Aufruf wird dadurch ein wenig langsamer.


Sichtbarkeit

Im Gegensatz zu allgemeinen Klassenmitgliedern müssen polymorphe Klassenmitglieder über die Vererbungshierarchie dieselbe Sichtbarkeit haben. Daher wird das folgende Codefragment vom Compiler zurückgewiesen:

Class Basis 
  Protected Overridable Sub f() 
  End Sub 
End Class 
Class Kind : Inherits Basis 
  Public Overrides Sub f()   'Compilerfehler: nicht identische Sichtbarkeit 
  End Sub 
End Class

Eine Besonderheit ergibt sich für eine Kindklasse, die ein polymorphes Klassenmitglied in einer anderen Anwendung überschreibt:


Protected Friend Overridable muss in einer anderen Anwendung (Projekt) mit Protected Overrides überschrieben werden (wenn die Polymorphie erhalten bleiben soll).


Inhomogene Mengen

Die Polymorphie ist besonders nützlich bei Sammlungen von Objekten verschiedenen Typs mit derselben Basisklasse. Da ein Array homogen sein muss, bleibt nichts anderes übrig, als Referenzen vom Typ der Basisklasse zu speichern. Das ist die Gemeinsamkeit der Objekte. Polymorphe Klassenmitglieder sind dadurch nicht eingeschränkt, da sie auch dann den Typ des Objekts berücksichtigen, wenn nur eine Referenz auf die Basisklasse vorliegt. Das folgende Codefragment nutzt die kleine Druckerhierarchie:


'...\Klassendesign\Polymorphie\Polymorph.vb

Option Strict On 
Namespace Klassendesign 
  ... 
  Module Polymorph 
    ... 
    Sub Menge() 
      Dim dr(4) As FlexiblerDrucker 
      Dim rd As Random = New Random(348) 
      For no As Integer = 0 To 4 
        If rd.NextDouble() < 0.5 Then 
          dr(no) = New FlexiblerDrucker() 
        Else 
          dr(no) = New Laserdrucker() 
        End If 
      Next 
      For Each d As FlexiblerDrucker In dr 
        d.ÜberschriftDrucken(d.GetType().Name) 
      Next 
      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Die Ausgabe zeigt, dass die typrichtige Druckmethode verwendet wird:

L a s e r d r u c k e r 
L a s e r d r u c k e r 
FlexiblerDrucker 
L a s e r d r u c k e r 
FlexiblerDrucker

Aufruf über Basisklassenreferenz

Soll in einer polymorphen Methode auf die Methode der Basisklasse zurückgegriffen werden, kann dies nicht direkt erfolgen, denn der Aufruf würde polymorph wieder die Methode selbst erreichen: eine infinite Rekursion. Genau dasselbe passiert bei dem Versuch, über eine Typumwandlung an die Basisklassenmethode mit CType(Me, Basis).Methode() heranzukommen. Man kann dann nur mit MyBase.Methode() auf die nächstgelegene Basisklassendefinition der Methode zugreifen. Ein Überspringen einer Klasse in der Hierarchie ist also nicht möglich.

Das folgende Codefragment testet den Aufruf einer polymorphen Methode über eine Basisklassenreferenz (CType(Me, Basis)) von innerhalb der Methode. Zur Verdeutlichung ist Object als Elternklasse explizit angegeben. Ein Auffangen der Ausnahme StackOverflowException gelingt nur selten, da durch den Überlauf der Speicher meistens bereits so voll ist, dass für einen sauberen Ausstieg über Catch nicht genügend Speicher zur Verfügung steht. Daher verwendet das Beispiel den Zähler rec.


'...\Klassendesign\Polymorphie\MyBase.vb

Option Strict On 
Namespace Klassendesign

  Class Typ : Inherits Object 
    Private rec As Integer

    Public Overrides Function ToString() As String 
      rec += 1 
      If rec > 100 Then Return "Gescheitert!" _ 
      Else Return CType(Me, Object).ToString() 
    End Function 
  End Class

  Module Infinit 
    Sub Test() 
      Dim t As Object = New Typ() 
      Console.WriteLine(t) 
      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Die Ausgabe zeigt das Scheitern:

Gescheitert!

Rheinwerk Computing - Zum Seitenanfang

3.14.2 Unterbrechen: Overloads, Shadows und Overrides Zur nächsten ÜberschriftZur vorigen Überschrift

Wird ein Klassenmitglied mit Overloads oder Shadows gekennzeichnet, unterbricht dies die Polymorphie an dieser Stelle. Die Polymorphie bis zu der Elternklasse ist nicht betroffen, genauso wenig wie der Zugriff auf die Elternklasse. Ebenso ist es möglich, mit den Kombinationen Oveloads Overridable sowie Shadows Overridable eine neue Polymorphie zu beginnen. Es kommt nicht oft vor, dass eine Unterbrechung der Polymorphie angemessen ist. Sie sollten deshalb genau prüfen, ob Ihr Klassendesign sich nicht besser formulieren lässt.

Das folgende Codefragment zeigt eine Anwendung, in der der Bruch der dynamischen Bindung durch die verschiedene Arbeitsweise eines Antriebs in oder außerhalb der Erdatmosphäre bewirkt wird. Allen Antrieben ist gemeinsam, dass sie betankt werden müssen, und daher wird der Methodenname trotz des Bruchs beibehalten. Anders ist die Notwendigkeit, außerhalb der Erdatmosphäre einen Ersatz für die Luft mitzunehmen. Die Linie rechts vom Code deutet die Bereiche an, über die jeweils die Polymorphie funktioniert. Die allgemeine Rakete hat einen Hybridantrieb, und der Atmosphärenteil wird durch den Aufruf von MyBase.Verbrennung() simuliert.


'...\Klassendesign\Polymorphie\OverloadsUndOverrides.vb

'...\Klassendesign\Polymorphie\OverloadsUndOverrides.vb

Option Strict On 
Namespace Klassendesign

  Class Triebwerk 
    Sub Verbrennung(ByVal art As String, ByVal kraftstoff As String, _ 
    Optional ByVal gas As String = "Luft") 
      Console.WriteLine("{0,9} als {1,9} verbrennt {2,11} mit {3,10}", _ 
                        Me.GetType().Name, art, kraftstoff, gas) 
    End Sub

    Overridable Sub Verbrennung()                       '+-- 
      Verbrennung("Triebwerk", "Brennstoff")            '| 
    End Sub                                             '| 
  End Class                                             '|

  Class Düse : Inherits Triebwerk                       '| 
    Overrides Sub Verbrennung()                         '| 
      Verbrennung("Düse", "Kerosin")                    '| 
    End Sub                                             '| 
  End Class                                             '|

  Class Rakete : Inherits Düse                          '| 
    Overridable Overloads Sub Verbrennung()             '| 
      MyBase.Verbrennung()                              'Å-- Atmosphäre 
      Verbrennung("Rakete", "Brennstoff", "Oxidator")   '| 
    End Sub                                             '| 
  End Class                                             '|

  Class Ariane : Inherits Rakete                        '| 
    Overrides Sub Verbrennung()                         '| 
      Verbrennung("Ariane", "Wasserstoff", "Sauerstoff")'| 
    End Sub                                             '| 
  End Class                                             '? 
  ... 
End Namespace

Durch verschiedene Datentypen in den For Each-Schleifen wird die Polymorphie getestet:


'...\Klassendesign\Polymorphie\OverloadsUndOverrides.vb

Option Strict On 
Namespace Klassendesign 
  ... 
  Module Unterbrochen 
    Sub Test() 
      Dim er() As Triebwerk = {New Triebwerk(), New Düse(), _ 
                               New Rakete(), New Ariane()}

      Console.WriteLine("--Referenz auf Triebwerk--") 
      For Each t As Triebwerk In er : t. Verbrennung() : Next

      Console.WriteLine("--Referenz auf Düse--") 
      For Each t As Düse In er.Skip(1) : t. Verbrennung() : Next

      Console.WriteLine("--Referenz auf Rakete--") 
      For Each t As Rakete In er.Skip(2) : t. Verbrennung() : Next

      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Die Ausgabe zeigt, dass eine Referenz von einem Typ, der oberhalb des Bruchs der dynamischen Bindung liegt (Triebwerk und Düse) in der Polymorphie vor dem Bruch (Rakete) gestoppt wird. Die Rakete taucht im dritten Block zweifach auf, da durch MyBase.Verbrennung() eine weitere Ausgabe erzeugt wird. Eine Alternative ist der Aufruf CType(Me, Triebwerk).Verbrennung(), der durch den Bruch der Polymorphie auch bei der Düse endet.

--Referenz auf Triebwerk-- 
Triebwerk als Triebwerk verbrennt  Brennstoff mit       Luft 
     Düse als      Düse verbrennt     Kerosin mit       Luft 
   Rakete als      Düse verbrennt     Kerosin mit       Luft 
   Ariane als      Düse verbrennt     Kerosin mit       Luft

--Referenz auf Düse-- 
     Düse als      Düse verbrennt     Kerosin mit       Luft 
   Rakete als      Düse verbrennt     Kerosin mit       Luft 
   Ariane als      Düse verbrennt     Kerosin mit       Luft

--Referenz auf Rakete-- 
   Rakete als      Düse verbrennt     Kerosin mit       Luft 
   Rakete als    Rakete verbrennt  Brennstoff mit   Oxidator 
   Ariane als    Ariane verbrennt Wasserstoff mit Sauerstoff

Ohne Overrides = Shadows

Wenn Sie beim Überschreiben vergessen, Overrides zu spezifizieren, ist dies gleichbedeutend mit Shadows. Dieses verdeckt alle Signaturen des Symbols in der Elternklasse, nicht nur die gerade neu definierte. Damit ist dann natürlich auch Schluss mit der Polymorphie. Das folgende Codefragment hat in der abgeleiteten Klasse Konditor keine Overrides-Spezifikation für die Methode Backen(). In der Methode Test() wird ein Konditor als Bäcker und Konditor angesprochen. Der auskommentierte Aufruf ist nicht erlaubt, da das implizite Shadows den Zugriff auf Bäcker.Backen() über eine Konditor-Referenz unterbindet (siehe Abschnitt 3.13.4, »Modifikation: Shadows und Overloads (Overrides)«).


'...\Klassendesign\Polymorphie\OverloadsUndOverrides.vb

Option Strict On 
Namespace Klassendesign 
  Class Bäcker 
    Overridable Sub Backen() 
      Console.WriteLine("Brot backen.") 
    End Sub

    Overridable Sub Backen(ByVal klein As Boolean) 
      If klein Then Console.WriteLine("Brötchen backen.") Else Backen() 
    End Sub 
  End Class

  Class Konditor : Inherits Bäcker 
    Sub Backen() 
      Console.WriteLine("Teilchen backen.") 
    End Sub 
  End Class

  Module OhneOverrides 
    Sub Test()

      Dim k As Konditor = New Konditor() 
      Dim b As Bäcker = k

      b.Backen() : k.Backen() 
      b.Backen(False) ' k.Backen(False) existiert nicht!

      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Die Ausgabe bestätigt, dass die Polymorphie unterbrochen ist.

Brot backen. 
Teilchen backen. 
Brot backen.

Rheinwerk Computing - Zum Seitenanfang

3.14.3 Unterbinden: NotInheritable, NotOverridable und MyClass Zur nächsten ÜberschriftZur vorigen Überschrift

So vielfältig die Möglichkeiten der Vererbung und der dynamischen Bindung auch sein mögen, manchmal möchten Sie diesen Variantenreichtum beschneiden. Zum Beispiel sollte eine Klasse zur Buchhaltung nicht veränderbar sein, damit die Revisionssicherheit erhalten bleibt. In anderen Fällen wollen Sie gut getestete Methoden vor Veränderungen schützen, ohne die Option zu versperren, der Klasse neue Methoden hinzuzufügen. Um diese Beschränkungen im Rahmen der Vererbung zu ermöglichen, kann die Polymorphie in drei Stufen unterbunden werden:

  • NotInheritable: Die Klassenhierarchie wird beendet.
  • NotOverridable: Ein einzelnes Klassenmitglied ist unveränderbar.
  • MyClass: Nicht-polymorpher Zugriff in der aktuellen Klasse.

NotInheritable

Der heftigste Eingriff in die Vererbung ist das Verhindern jeglicher Kindklassen. Haben Sie beispielsweise folgende Vererbungshierarchie, ist es nicht sinnvoll, von der untersten Ebene weiter abzuleiten, da die Software für eine Hardware gedacht ist, die mit dem Druckermodell festliegt. Eine neue Druckerhardware braucht auch andere Steuerbefehle, und es wäre fehlerträchtig, auf dem anderen Druckermodell aufzubauen.

Drucker 
  +-Laserdrucker 
      +-Apple Laserwriter

Das nächste Codefragment zeigt eine andere Anwendung für dieses Konzept. Von einer Klasse Login mit einer Kennwortprüfung können durch NotInheritable keine Kindklassen erzeugt werden, da der Schutz sonst einfach durch eine Neudefinition der Prüfung in einer Kindklasse ausgehebelt werden könnte (zur Erinnerung: eine Kindklasse ist ein vollwertiger Stellvertreter). Da versucht wird, mit Superuser eine Kindklasse zu erzeugen, wird das Beispiel vom Compiler nicht fehlerfrei übersetzt.


'...\Klassendesign\Polymorphie\NotInheritable.vb

Option Strict On 
Namespace Klassendesign

  NotInheritable Class Login 
    Private kennwort As String 
    Sub New(ByVal kennwort As String) 
      Me.kennwort = kennwort 
    End Sub

    Function Berechtigt(ByVal kennwort As String) As Boolean 
      Return Me.kennwort = kennwort 
    End Function 
  End Class

  Class Superuser 
    Inherits Login                                    'Compilerfehler!!

    Function Berechtigt(ByVal k As String) As Boolean 
      Return True 
    End Function 
  End Class 
End Namespace

Hinweis
Um den Code besser lesbar zu halten, sollte NotInheritable in jedem Teil einer mit Partial aufgeteilten Klasse angegeben werden.


NotOverridable

Wollen Sie nur eine Variante eines polymorphen Klassenmitglieds gegen Änderungen in Kindklassen sperren, kennzeichnen Sie diese mit NotOverridable. Dabei ist die Sperrung nicht beliebig:

  • Ausschließlich mit Overrides überschreibende Signaturen können gesperrt werden.
  • Nur diese eine Signatur ist gesperrt.
  • Ein gleichzeitiges Überdecken aller Signaturen desselben Symbols in einer Kindklasse mit Shadows ist erlaubt, die Sperrung ist also nicht absolut.
  • Mit NotInheritable gesperrte Klassen dürfen NotOverridable nicht verwenden.

Die folgenden drei Codefragmente zeigen die Beschneidung der Vererbungsfähigkeit mit NotOverridable an einer Klassenhierarchie, die die Wanddicken verschiedener Gebäudetypen ermittelt. Da eine Signatur nur dann gesperrt werden kann, wenn sie mit Overrides gekennzeichnet ist, wird in einer Klasse Gebäude die Methode Wand() mit MustOverride als abstrakt markiert und in der Klasse Haus mit Overrides überschrieben (eine Verwendung von Overrides in Gebäude hätte das Beispiel länger gemacht). Diese Signatur wird in Haus gleichzeitig mit NotOverridable gegen Überschreiben geschützt. Die Methode ist mit einem anderen Parametertyp überladen, um weiter unten in Kindklassen die Konsequenzen von NotOverridable der anderen Signatur deutlich zu machen.


'...\Klassendesign\Polymorphie\NotOverridable.vb

Option Strict On 
Namespace Klassendesign

  MustInherit Class Gebäude 
    MustOverride Sub Wand(ByVal material As String) 
  End Class

  Class Haus : Inherits Gebäude 
    NotOverridable Overrides Sub Wand(ByVal material As String) 
      Console.WriteLine("Wanddicke Haus " & If(material = "Beton", 20, 25)) 
    End Sub

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

Nun wird die Klasse Haus mit der NotOverridable-Signatur von zwei Klassen beerbt. Die Klasse Villa ändert die nicht gesperrte Methodensignatur und berücksichtigt, dass erst bei kleinerem Kostenrahmen mit Beton gearbeitet wird. Dies spiegelt die Realität wider, dass der Bauherr einer Villa mehr Wert auf das Ambiente legt, als er das bei einem »normalen« Haus tun würde, und einen Betonklotz vermeidet. Die Klasse berücksichtigt also die Besonderheiten des Gebäudetyps, ohne die Wanddicke ändern zu können, die aus Gründen der Statik gleich bleiben sollte. Der auskommentierte Versuch, die verschlossene Variante zu ändern, würde einen Compilerfehler erzeugen.

Die andere Klasse, Bude, entscheidet sich für einen Ersatz der Wanddickenberechnung, zum Beispiel aufgrund lockerer Baubestimmungen (»genehmigungsfreies Verfahren« …). Da eine Methodensignatur gesperrt ist, bleibt nichts anderes übrig, als alle Wand()-Signaturen komplett zu ersetzen und so die Polymorphie zu der Elternklasse zu kappen. Da durch den Modifikator NotInheritable keine Kindklassen von Bude möglich sind, überschatten die Methoden mit Overloads die Elternklassensignaturen nichtpolymorph. Sind Kindklassen erlaubt, ist Overridable die bessere Wahl.


'...\Klassendesign\Polymorphie\NotOverridable.vb

Option Strict On 
Namespace Klassendesign 
  ... 
  Class Villa : Inherits Haus 
    'Overrides Sub Wand(ByVal material As String)          'Compilerfehler!! 
    '  Console.WriteLine("Wanddicke Haus " & If(material = "Beton", 40, 50)) 
    'End Sub

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

  NotInheritable Class Bude : Inherits Haus 
    Overloads Sub Wand(ByVal material As String)      'implizit überdeckend! 
      Console.WriteLine("Wanddicke Bude " & If(material = "Beton", 15, 20)) 
    End Sub

    Overloads Sub Wand(ByVal kostenrahmen As Double)  'implizit überdeckend! 
      Console.Write("Materialwahl Bude : ") 
      Wand(If(kostenrahmen <= 20, "Beton", "Stein")) 
    End Sub 
  End Class 
  ... 
End Namespace

Im folgenden dritten Teil wird die kleine Vererbungshierarchie getestet. Um die polymorphen Aspekte zu erfassen, werden eine Villa und eine Bude auch unter einer Basisklassenreferenz vom Typ Haus gespeichert. Über alle vier Variablen wird zuerst die in Haus gesperrte Signatur von Wand() aufgerufen, danach die nicht gesperrte, die in Villa und Bude überladen ist.


'...\Klassendesign\Polymorphie\NotOverridable.vb

Option Strict On 
Namespace Klassendesign 
  ... 
  Module StrukturErhalten 
    Sub Test() 
      Dim villa As Villa = New Villa() : Dim villa_h As Haus = villa 
      Dim bude As Bude = New Bude() : Dim bude_h As Haus = bude

      Dim material As String = "Beton" 
      Console.Write("Villa         : ") : villa.Wand(material) 
      Console.Write("Bude          : ") : bude.Wand(material) 
      Console.Write("Villa als Haus: ") : villa_h.Wand(material) 
      Console.Write("Bude  als Haus: ") : bude_h.Wand(material)

      Dim kostenrahmen As Double = 15 
      Console.Write("Villa         : ") : villa.Wand(kostenrahmen) 
      Console.Write("Bude          : ") : bude.Wand(kostenrahmen) 
      Console.Write("Villa als Haus: ") : villa_h.Wand(kostenrahmen) 
      Console.Write("Bude  als Haus: ") : bude_h.Wand(kostenrahmen)

      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Die Ausgabe zeigt, dass alle Basisklassenreferenzen (»als Haus« in der Ausgabe) und die Villa die Wanddicken aus der Klasse Haus nehmen, da die Villa durch NotOverridable und die Bude durch die implizite Abschattung mit Overloads von der dynamischen Bindung abgeschnitten sind. Die Klasse Villa überschreibt eine Signatur, sodass die Polymorphie für die Materialwahl erhalten bleibt, während durch die Abschattung bei der Basisklassenreferenz der Bude die Polymorphie in der Klasse Haus endet.

Villa         : Wanddicke Haus 20 
Bude          : Wanddicke Bude 15 
Villa als Haus: Wanddicke Haus 20 
Bude  als Haus: Wanddicke Haus 20

Villa         : Materialwahl Villa: Wanddicke Haus 25 
Bude          : Materialwahl Bude : Wanddicke Bude 15 
Villa als Haus: Materialwahl Villa: Wanddicke Haus 25 
Bude  als Haus: Materialwahl Haus : Wanddicke Haus 20

MyClass

Wenn die dynamische Bindung je nach Situation erwünscht oder unerwünscht ist, kann sie durch explizite Spezifikation von MyClass unterbunden werden. Alle nicht derart markierten Aufrufe und Eigenschaftszugriffe werden in ihrem polymorphen Verhalten nicht beeinflusst. Sie steuern also bei der Nutzung eines Klassenmitglieds, ob von der Polymorphie Gebrauch gemacht werden soll.


MyClass.Mitglied unterdrückt die Polymorphie.


Das nächste Codefragment definiert eine zweistufige Vererbungshierarchie mit einer polymorphen Methode Info(). Durch die Angabe von MyClass in der Methode Allgemein() wird die dynamische Bindung unterdrückt. Der Aufruf der Methoden erfolgt innerhalb von Test().


'...\Klassendesign\Polymorphie\MyClass.vb

Option Strict On 
Namespace Klassendesign

  Class Dozent 
    Overridable Sub Info() 
      Console.WriteLine("Dozent") 
    End Sub

    Sub Allgemein() 
      MyClass.Info() 
    End Sub 
  End Class

  Class Professor : Inherits Dozent 
    Overrides Sub Info() 
      Console.WriteLine("Professor") 
    End Sub 
  End Class

  Module OhnePolymorphie 
    Sub Test() 
      Dim d As Dozent = New Professor()

      d.Info() 
      d.Allgemein()

      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Die erste Ausgabe dient zur Kontrolle der korrekten Polymorphie. Die zweite Ausgabe zeigt, dass die Polymorphie durch MyClass in der Methode Allgemein() unterbunden wird.

Professor 
Dozent

Rheinwerk Computing - Zum Seitenanfang

3.14.4 Konstruktoren Zur nächsten ÜberschriftZur vorigen Überschrift

In der Konstruktorlogik kann sich eine Stolperfalle durch die Polymorphie ergeben. Wird in einem Konstruktor einer Basisklasse eine mit Overridable dynamisch gebundene Methode oder Eigenschaft verwendet, kommt sie zum Zuge, bevor der Konstruktor der Kindklasse eine Chance hat, Objektzustände vollständig zu initialisieren. Dadurch stehen für diese nur typangepasste »Nullen« zur Verfügung. In Abschnitt 2.5.6, »Initialisierung von Variablen«, steht, was in diesem Zusammenhang unter einer »Null« zu verstehen ist. In den seltensten Fällen ist das gewünscht. Im folgenden Codefragment wird versucht, im Konstruktor der Basisklasse Himmelskörper mit der polymorphen Methode Dichte() die Dichte auszugeben. Wenn das Objekt als Planet vom Typ der Kindklasse ist, wird der Aufruf in der Kindklasse verwendet. Er greift auf die noch nicht initialisierte masse zu. Die Ausführungsreihenfolge ist durch die Zahlen am Zeilenende gekennzeichnet.


'...\Klassendesign\Polymorphie\Konstruktoren.vb

Option Strict On 
Namespace Klassendesign 
  Class Himmelskörper 
    Protected volumen As Double

    Sub New(ByVal radius As Double)                                      '4 
      Me.volumen = 4 / 3 * Math.PI * radius ^ 3 
      Dichte()                                                           '5 
    End Sub

    Overridable Sub Dichte() 
    End Sub 
  End Class

  Class Planet : Inherits Himmelskörper 
    Private masse As Double

    Sub New(ByVal volumen As Double, ByVal masse As Double)              '2 
      MyBase.New(volumen)                                                '3 
      Me.masse = masse                                                   '7 
    End Sub

    Overrides Sub Dichte()                                               '6 
      Console.WriteLine("Dichte: {0}", masse / volumen) 
    End Sub 
  End Class

  Module Konstruktoren 
    Sub Test() 
      Dim erde As Planet = New Planet(12735 * 1000 / 2, 5.974 * 10 ^ 24) '1 
      erde.Dichte() 
      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Die erste Ausgabe stammt aus dem Aufruf von Dichte() im Konstruktor von Planet, nutzt den Standardwert 0 von masse und berechnet daraufhin eine Dichte von 0. Dagegen ist die Berechnung nach Abschluss der Objektinitialisierung korrekt, wie die zweite Ausgabe zeigt:

Dichte: 0 
Dichte: 5524.20450952082

Dieses Beispiel zeigt deutlich:


Sie sollten keine virtuellen Methoden im Konstruktor aufrufen.



Rheinwerk Computing - Zum Seitenanfang

3.14.5 Equals Zur nächsten ÜberschriftZur vorigen Überschrift

Oft müssen Objekte miteinander verglichen werden. Wenn es dabei nicht um Identität, sondern um Gleichwertigkeit geht, sollte der Vergleichsoperator = oder die Methode Equals() der Klasse Object verwendet werden. Damit der Vergleich mit Equals() auch für Basisklassenreferenzen »wunschgemäß« läuft, sollte die Methode mit Overrides polymorph überschrieben werden. Da Operatoren nicht polymorph sind, ist der Vergleich mit Equals() flexibler. Da der Typ, mit dem verglichen wird, immer Object ist, müssen oft Typumwandlungen innerhalb der Methode vorgenommen werden. Im folgenden Codefragment steht explizit Object als Elternklasse, um die Vererbungshierarchie zu unterstreichen. Für die Klasse Kekse wird Equals() so überschrieben, dass ein Kalorienvergleich mit Objekten vom Typ Kekse oder Torte möglich ist. In der Methode Test() werden mit Equals() Vergleiche in beide Richtungen durchgeführt.


'...\Klassendesign\Polymorphie\OverloadsUndOverrides.vb

Option Strict On 
Namespace Klassendesign

  Class Kekse : Inherits Object 
    Friend kal As Integer = 600

    Public Overrides Function Equals(ByVal obj As Object) As Boolean 
      If TypeOf obj Is Kekse Then Return CType(obj, Kekse).kal = kal 
      If TypeOf obj Is Torte Then Return CType(obj, Torte).kal = kal 
      Return False 
    End Function 
  End Class

  Class Torte : Inherits Object 
    Friend kal As Integer = 600 
  End Class

  Module Gleichheit 
    Sub Test() 
      Dim bahlsen As Object = New Kekse 
      Dim sacher As Object = New Torte()

      Console.WriteLine("Kalorien gleich: {0}", bahlsen.Equals(sacher)) 
      Console.WriteLine("Kalorien gleich: {0}", sacher.Equals(bahlsen))

      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Die Ausgabe zeigt, dass der Vergleich nur in einer Richtung funktioniert, da die Klasse Torte die Methode Equals() nicht überschrieben hat:

Kalorien gleich: True 
Kalorien gleich: False

Hinweis
Equals() erlaubt Object als Parametertyp, im Gegensatz zum Operator =.



Hinweis
Mit Equals() sollte auch immer GetHashCode() überschrieben werden.



Rheinwerk Computing - Zum Seitenanfang

3.14.6 ToString Zur nächsten ÜberschriftZur vorigen Überschrift

Nicht nur bei Konsolenanwendungen muss häufig ein Objekt am Bildschirm »dargestellt« werden. Die einfachste Möglichkeit hierzu ist eine Repräsentation als Zeichenkette, da die Klassenbibliotheken von .NET eine breite Unterstützung für die Darstellung von Texten bieten. In einer Konsole ist sie sogar die einzige. Auch für Kontrollausdrucke während der Testphase ist eine Zeichenkette sehr bequem. Wegen der großen Bedeutung der Zeichenkettendarstellung ist in der Mutter aller Klassen, Object, eine Methode namens ToString() enthalten, die ein Objekt als Zeichenkette darstellt. Weil diese Methode über Ihre Klassen nichts wissen kann, ist die Darstellung nicht besonders hilfreich. Die einzige Gemeinsamkeit aller Objekte ist die Tatsache, dass sie einen Typ besitzen. Dessen Name wird von ToString() zurückgegeben. Da nur der Autor einer Klasse eine sinnvolle Darstellung als Zeichenkette bewirken kann, sind Sie dafür verantwortlich, in der ToString()-Methode die »richtige« Funktionalität bereitzustellen.

Das folgende Codefragment fasst die Elemente eines beliebigen Vektors in geschweiften Klammern zusammen. Jedes Einzelelement wird mit ToString() in eine Zeichenkette umgewandelt, was automatisch auch geschachtelte Vektoren erfasst. Nullreferenzen werden gesondert behandelt. Nur zur Verdeutlichung wird Inherits Object angegeben. Die Verwendung von Overrides ist hier notwendig, da WriteLine() der Vektor als vom Typ Object übergeben wird. Würde dann Shadows oder Overloads für ToString() in Vektor verwendet, wäre die Polymorphie unterbrochen mit der Konsequenz, dass die Methode von Object aus nicht »sichtbar« wäre und die generische Methode Object.ToString() nur den Datentyp ausgeben würde.


'...\Klassendesign\Polymorphie\ToString.vb

Option Strict On 
Namespace Klassendesign 
  Class Vektor : Inherits Object 
    Private werte() As Object 
    Sub New(ByVal ParamArray werte() As Object) 
      Me.werte = werte 
    End Sub

    Overrides Function ToString() As String 
      Dim text As String = "{" 
      For no As Integer = 0 To werte.Length – 1 
        text += If(no > 0, ", ", "") 
        text += If(werte(no) Is Nothing, "-", werte(no).ToString()) 
      Next 
      Return text + "}" 
    End Function 
  End Class

  Module Zeichenkettendarstellung 
    Sub Test() 
      Dim v As Vektor = New Vektor( _ 
        3.14, New Vektor(5, New System.Random()), Nothing, New Exception()) 
      Console.WriteLine("Vektor {0}", v) 
      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Die Ausgabe zeigt, wie der geschachtelte Vektor als zweites Klammerpaar erscheint. Die Klasse Random hat keine spezielle Definition für ToString() und nutzt die von Object, die den Datentyp ausgibt. Die Klasse Exception hat für ToString() eine angepasste Definition.

Vektor {3.14, {5, System.Random}, -, System.Exception: Exception of type  
  'System.Exception' was thrown.}

Rheinwerk Computing - Zum Seitenanfang

3.14.7 Grafikbibliothek: Objektsammlungen 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.13.9, »Grafikbibliothek: neue Vielecke«, erweiterte Grafikanwendung. Zum Vieleck wird die Eckenzahl als Eigenschaft hinzugefügt. Die Ausnahme sorgt dafür, dass die Programmlogik für den Fall erhalten bleibt, dass eine Kindklasse von Vieleck das Überschreiben der Eigenschaft vergisst. Dies ist eine (schlechte) Alternative zu MustOverride, von der im .NET Compact Framework für mobile Geräte leider oft Gebrauch gemacht wird, um die äußere Form der Klassen kompatibel zum normalen .NET zu halten. In der Klasse Rechteck wird die Eigenschaft mit NotOverridable gekennzeichnet, da alle von Rechteck abgeleiteten Klassen immer vier Ecken haben müssen.


'...\Klassendesign\Graphik\Polymorphie.vb

Option Explicit On 
Namespace Klassendesign

  Partial Public MustInherit Class Vieleck 
    Overridable ReadOnly Property Eckenzahl() As Integer 
      Get 
        Throw New NotImplementedException("Kein konkretes Vieleck!") 
      End Get 
    End Property 
  End Class

  Partial Public Class Rechteck : Inherits Vieleck 
    NotOverridable Overrides ReadOnly Property Eckenzahl() As Integer 
      Get 
        Return 4 
      End Get 
    End Property 
  End Class 
  ... 
End Namespace

Zur bequemeren Ausgabe wird noch ToString() überschrieben. Bis auf die Angabe des Rechtecktyps kann alles in der Klasse Rechteck ablaufen. Das Quadrat nimmt mit MyBase Rückgriff auf diese Implementation, die durch den If-Operator die Zeichenfolge »Rechteck« nicht zurückgibt. Schließlich ist die Klasse Quadrat noch mit NotInheritable gekennzeichnet, da speziellere Quadrate nicht möglich sein sollen.


'...\Klassendesign\Graphik\Polymorphie.vb

Option Explicit On 
Namespace Klassendesign 
  ... 
  Partial Public Class Rechteck : Inherits Vieleck 
    Public Overrides Function ToString() As String 
      Return If(Me.GetType() Is GetType(Rechteck), "Rechteck ", "") & _ 
             "{" & a & "," & b & "}" 
    End Function 
  End Class

  Partial Public NotInheritable Class Quadrat : Inherits Rechteck 
    Public Overrides Function ToString() As String 
      Return "Quadrat " & MyBase.ToString() 
    End Function 
  End Class

End Namespace

Zum Test wird eine Liste von Quadraten erzeugt und in einer Schleife ausgegeben.


'...\Klassendesign\Zeichner\Polymorphie.vb

Option Explicit On 
Namespace Klassendesign 
  Partial Class Zeichner 
    Sub Polymorphie() 
      Dim rechtecke() As Rechteck = {New Quadrat(7), New Rechteck(6, 8)} 
      For Each r As Rechteck In rechtecke 
        Console.WriteLine(r.ToString() & " mit " & r.Eckenzahl & " Ecken") 
      Next 
    End Sub 
  End Class 
End Namespace

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

Quadrat {7,7} mit 4 Ecken 
Rechteck {6,8} mit 4 Ecken

Die nächste Erweiterung erfolgt in Abschnitt 3.15.5, »Grafikbibliothek: Flächen«.



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