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

Inhaltsverzeichnis
Vorwort zur 6. Auflage
1 Allgemeine Einführung in .NET
2 Grundlagen der Sprache C#
3 Das Klassendesign
4 Vererbung, Polymorphie und Interfaces
5 Delegates und Ereignisse
6 Strukturen und Enumerationen
7 Fehlerbehandlung und Debugging
8 Auflistungsklassen (Collections)
9 Generics – Generische Datentypen
10 Weitere C#-Sprachfeatures
11 LINQ
12 Arbeiten mit Dateien und Streams
13 Binäre Serialisierung
14 XML
15 Multithreading und die Task Parallel Library (TPL)
16 Einige wichtige .NET-Klassen
17 Projektmanagement und Visual Studio 2012
18 Einführung in die WPF und XAML
19 WPF-Layout-Container
20 Fenster in der WPF
21 WPF-Steuerelemente
22 Elementbindungen
23 Konzepte von WPF
24 Datenbindung
25 Weitere Möglichkeiten der Datenbindung
26 Dependency Properties
27 Ereignisse in der WPF
28 WPF-Commands
29 Benutzerdefinierte Controls
30 2D-Grafik
31 ADO.NET – Verbindungsorientierte Objekte
32 ADO.NET – Das Command-Objekt
33 ADO.NET – Der SqlDataAdapter
34 ADO.NET – Daten im lokalen Speicher
35 ADO.NET – Aktualisieren der Datenbank
36 Stark typisierte DataSets
37 Einführung in das ADO.NET Entity Framework
38 Datenabfragen des Entity Data Models (EDM)
39 Entitätsaktualisierung und Zustandsverwaltung
40 Konflikte behandeln
41 Plain Old CLR Objects (POCOs)
Stichwort

Download:
- Beispiele, ca. 62,4 MB

Jetzt Buch bestellen
Ihre Meinung?

Spacer
Visual C# 2012 von Andreas Kühnel
Das umfassende Handbuch
Buch: Visual C# 2012

Visual C# 2012
Rheinwerk Computing
1402 S., 6., aktualisierte und erweiterte Auflage 2013, geb., mit DVD
49,90 Euro, ISBN 978-3-8362-1997-6
Pfeil 4 Vererbung, Polymorphie und Interfaces
Pfeil 4.1 Die Vererbung
Pfeil 4.1.1 Basisklassen und abgeleitete Klassen
Pfeil 4.1.2 Die Ableitung einer Klasse
Pfeil 4.1.3 Klassen, die nicht abgeleitet werden können
Pfeil 4.1.4 Konstruktoren in abgeleiteten Klassen
Pfeil 4.1.5 Der Zugriffsmodifizierer »protected«
Pfeil 4.1.6 Die Konstruktorverkettung in der Vererbung
Pfeil 4.2 Der Problemfall geerbter Methoden
Pfeil 4.2.1 Geerbte Methoden mit »new« verdecken
Pfeil 4.2.2 Abstrakte Methoden
Pfeil 4.2.3 Virtuelle Methoden
Pfeil 4.3 Typumwandlung und Typuntersuchung von Objektvariablen
Pfeil 4.3.1 Die implizite Typumwandlung von Objektreferenzen
Pfeil 4.3.2 Die explizite Typumwandlung von Objektreferenzen
Pfeil 4.3.3 Typuntersuchung mit dem »is«-Operator
Pfeil 4.3.4 Typumwandlung mit dem »as«-Operator
Pfeil 4.4 Polymorphie
Pfeil 4.4.1 Die »klassische« Methodenimplementierung
Pfeil 4.4.2 Abstrakte Methoden
Pfeil 4.4.3 Virtuelle Methoden
Pfeil 4.5 Weitere Gesichtspunkte der Vererbung
Pfeil 4.5.1 Versiegelte Methoden
Pfeil 4.5.2 Überladen einer Basisklassenmethode
Pfeil 4.5.3 Statische Member und Vererbung
Pfeil 4.5.4 Geerbte Methoden ausblenden?
Pfeil 4.6 Das Projekt »GeometricObjectsSolution« ergänzen
Pfeil 4.6.1 Die Klasse »GeometricObject«
Pfeil 4.7 Eingebettete Klassen (Nested Classes)
Pfeil 4.8 Interfaces (Schnittstellen)
Pfeil 4.8.1 Einführung in die Schnittstellen
Pfeil 4.8.2 Die Schnittstellendefinition
Pfeil 4.8.3 Die Schnittstellenimplementierung
Pfeil 4.8.4 Die Interpretation der Schnittstellen
Pfeil 4.8.5 Änderungen am Projekt »GeometricObjects«
Pfeil 4.9 Das Zerstören von Objekten – der »Garbage Collector«
Pfeil 4.9.1 Die Arbeitsweise des Garbage Collectors
Pfeil 4.9.2 Expliziter Aufruf des Garbage Collectors
Pfeil 4.9.3 Der Destruktor
Pfeil 4.9.4 Die »IDisposable«-Schnittstelle
Pfeil 4.9.5 Die Ergänzungen in den Klassen »Circle« und »Rectangle«

Rheinwerk Computing - Zum Seitenanfang

4.4 PolymorphieZur nächsten Überschrift

In Abschnitt 4.2 haben Sie erfahren, dass die Methode Starten in der Klasse Luftfahrzeug unterschiedlich bereitgestellt werden kann. Es ist nun an der Zeit, darauf einzugehen, welche Konsequenzen die drei Varianten haben.

Dazu schreiben wir in der Main-Methode zunächst Programmcode, mit dem abstrakt, virtuell und klassisch implementierte Methoden getestet werden sollen.

static void Main(string[] args) {
Luftfahrzeug[] arr = new Luftfahrzeug[4];
arr[0] = new Flugzeug();
arr[1] = new Hubschrauber();
arr[2] = new Hubschrauber();
arr[3] = new Flugzeug();
foreach(Luftfahrzeug temp in arr) {
temp.Starten();
}
Console.ReadLine();
}

Listing 4.15 Code, um die Methode »Starten« zu testen

Zuerst wird ein Array vom Typ Luftfahrzeug deklariert. Jedes Array-Element ist vom Typ Luftfahrzeug. Weil die Klassen Flugzeug und Hubschrauber von diesem Typ abgeleitet sind, kann jedem Array-Element nach der Regel der impliziten Konvertierung auch die Referenz auf ein Objekt vom Typ der beiden Subklassen zugewiesen werden:

arr[0] = new Flugzeug();
arr[1] = new Hubschrauber();
[...]

Danach wird innerhalb einer foreach-Schleife auf alle Array-Elemente die Methode Starten aufgerufen. Die Laufvariable ist vom Typ Luftfahrzeug, also vom Typ der Basisklasse. In der Schleife wird auf diese Referenz die Starten-Methode aufgerufen.


Rheinwerk Computing - Zum Seitenanfang

4.4.1 Die »klassische« MethodenimplementierungZur nächsten ÜberschriftZur vorigen Überschrift

Wir wollen an dieser Stelle zunächst die klassische Methodenimplementierung in der Basisklasse testen. Die beiden ableitenden Klassen sollen die geerbte Methode Starten mit dem Modifizierer new überdecken:

public class Luftfahrzeug {
public void Starten() {
Console.WriteLine("Das Luftfahrzeug startet.");
}
}
public class Flugzeug : Luftfahrzeug {
public new void Starten() {
Console.WriteLine("Das Flugzeug startet.");
}
}

Listing 4.16 Testen der in der abgeleiteten Klasse überdeckenden Methode

Starten wir die Anwendung, wird die folgende Ausgabe viermal im Konsolenfenster angezeigt:

Das Luftfahrzeug startet.

Das Ergebnis ist zwar nicht spektakulär, hat aber weitreichende Konsequenzen. Wir müssen uns nämlich die Frage stellen, ob die Ausgabe das ist, was wir erreichen wollten. Vermutlich nicht, denn eigentlich sollte doch jeweils die typspezifische Methode Starten in der abgeleiteten Klasse ausgeführt werden.

Das ursächliche Problem ist das statische Binden des Methodenaufrufs an die Basisklasse. Statisches Binden heißt, dass die auszuführende Operation bereits zur Kompilierzeit festgelegt wird. Der Compiler stellt fest, von welchem Typ das Objekt ist, auf dem die Methode aufgerufen wird, und erzeugt den entsprechenden Code. Statisches Binden führt dazu, dass die Methode der Basisklasse aufgerufen wird, obwohl eigentlich die »neue« Methode in der abgeleiteten Klasse erforderlich wäre.

Das Beispiel macht deutlich, welchen Nebeneffekt das Überdecken einer Methode mit dem Modifizierer new haben kann: Der Compiler betrachtet das Objekt, als wäre es vom Typ der Basisklasse, und ruft die unter Umständen aus logischer Sicht sogar fehlerhafte Methode in der Basisklasse auf.


Rheinwerk Computing - Zum Seitenanfang

4.4.2 Abstrakte MethodenZur nächsten ÜberschriftZur vorigen Überschrift

Nun ändern wir den Programmcode in der Basisklasse Luftfahrzeug und stellen die Methode Starten als abstrakte Methode zur Verfügung. Die ableitenden Klassen erfüllen die Vertragsbedingung und überschreiben die geerbte Methode mit override. Am Programmcode in Main nehmen wir keine Änderungen vor.

public abstract class Luftfahrzeug {
public abstract void Starten();
}
public class Flugzeug : Luftfahrzeug {
public override void Starten() {
Console.WriteLine("Das Flugzeug startet.");
}
}

Listing 4.17 Testen der überschreibenden Methode

Ein anschließender Start der Anwendung bringt ein ganz anderes Ergebnis als im ersten Versuch:

Das Flugzeug startet.
Der Hubschrauber startet.
Der Hubschrauber startet.
Das Flugzeug startet.

Tatsächlich werden nun die typspezifischen Methoden aufgerufen.

Anscheinend ist die Laufvariable temp der foreach-Schleife in der Lage, zu entscheiden, welche Methode anzuwenden ist. Dieses Verhalten unterscheidet sich gravierend von dem, was wir im Zusammenhang mit den mit new ausgestatteten, überdeckenden Methoden zuvor gesehen haben. Die Bindung des Methodenaufrufs kann nicht statisch sein, sie erfolgt dynamisch zur Laufzeit.

Die Fähigkeit, auf einer Basisklassenreferenz die typspezifische Methode aufzurufen, wird als Polymorphie bezeichnet und ist neben der Kapselung und der Vererbung die dritte Säule der objektorientierten Programmierung. Polymorphie bezeichnet ein Konzept der Objektorientierung, das besagt, dass Objekte bei gleichen Methodenaufrufen unterschiedlich reagieren können. Dabei können Objekte verschiedener Typen unter einem gemeinsamen Oberbegriff (d. h. einer gemeinsamen Basis) betrachtet werden. Die Polymorphie sorgt dafür, dass der Methodenaufruf automatisch bei der richtigen, also typspezifischen Methode landet.

Polymorphie arbeitet mit dynamischer Bindung. Der Aufrufcode wird nicht zur Kompilierzeit erzeugt, sondern erst zur Laufzeit der Anwendung, wenn die konkreten Typinformationen vorliegen. Im Gegensatz dazu legt die statische Bindung die auszuführende Operation wie gezeigt bereits zur Kompilierzeit fest.


Rheinwerk Computing - Zum Seitenanfang

4.4.3 Virtuelle MethodenZur nächsten ÜberschriftZur vorigen Überschrift

Überschreibt eine Methode eine geerbte abstrakte Methode, zeigt die überschreibende Methode ausnahmslos immer polymorphes Verhalten. Wird in einer Basisklasse eine Methode »klassisch« implementiert und in der ableitenden Klasse durch eine Neuimplementierung mit new verdeckt, kann die verdeckende Methode niemals polymorph sein.

Vielleicht erahnen Sie an dieser Stelle schon, wozu virtuelle Methoden dienen. Erinnern wir uns: Eine Methode gilt als virtuell, wenn sie in der Basisklasse voll implementiert und mit dem Modifizierer virtual signiert ist, wie im folgenden Listing noch einmal gezeigt wird:

public class Luftfahrzeug {
public virtual void Starten() {
Console.WriteLine("Das Luftfahrzeug startet.");
}
}

Listing 4.18 Virtuelle Methode in der Basisklasse

Sie müssen eine virtuelle Methode als ein Angebot der Basisklasse an die ableitenden Klassen verstehen. Es ist das Angebot, die geerbte Methode entweder so zu erben, wie sie in der Basisklasse implementiert ist, sie bei Bedarf polymorph zu überschreiben oder eventuell auch einfach nur (nichtpolymorph) zu überdecken.

Polymorphes Überschreiben einer virtuellen Methode

Möchte die ableitende Klasse die geerbte Methode neu implementieren und soll die Methode polymorphes Verhalten zeigen, muss die überschreibende Methode mit dem Modifizierer override signiert werden, z. B.:

public class Flugzeug : Luftfahrzeug {
public override void Starten() {
Console.WriteLine("Das Flugzeug startet.");
}
}

Listing 4.19 Polymorphes Überschreiben einer geerbten virtuellen Methode

Das Ergebnis des Aufrufs von Starten auf eine Basisklassenreferenz ist identisch mit dem Aufruf einer abstrakten Methode: Es wird die typspezifische Methode ausgeführt. An dieser Stelle lässt sich sofort schlussfolgern, dass der Modifizierer override grundsätzlich immer Polymorphie signalisiert.

Nicht-polymorphes Überdecken einer virtuellen Methode

Soll eine ableitende Klasse eine geerbte virtuelle Methode nichtpolymorph überschreiben, kommt der Modifizierer new ins Spiel:

public class Flugzeug : Luftfahrzeug {
public new void Starten() {
Console.WriteLine("Das Flugzeug startet.");
}
}

Listing 4.20 Nicht-polymorphes Überschreiben einer geerbten virtuellen Methode

Die mit new neu implementierte virtuelle Methode zeigt kein polymorphes Verhalten, wenn wir die Testanwendung starten. Auch hier können wir unter Berücksichtigung des Verdeckens klassisch implementierter Methoden sagen, dass im Zusammenhang mit dem Modifizierer new niemals polymorphes Verhalten eintritt.

Weiter gehende Betrachtungen

Es ist möglich, innerhalb einer Vererbungskette ein gemischtes Verhalten von Ausblendung und Überschreibung vorzusehen, wie das folgende Codefragment zeigt:

public class Luftfahrzeug {
public virtual void Starten() { }
}
public class Flugzeug : Luftfahrzeug {
public override void Starten () { [...] }
}
public class Segelflugzeug : Flugzeug {
public new void Starten() { [...] }
}

Listing 4.21 Überschreiben und Ausblenden in einer Vererbungskette

Luftfahrzeug bietet die virtuelle Methode Starten an, und die abgeleitete Klasse Flugzeug überschreibt diese mit override polymorph. Die nächste Ableitung in Segelflugzeug überdeckt jedoch nur noch mit new. Wenn Sie nun nach der Zuweisung

Luftfahrzeug lfzg = new Segelflugzeug();

auf der Referenz lfzg die Methode Starten aufrufen, wird die Methode Starten in Flugzeug ausgeführt, da diese die aus Luftfahrzeug geerbte Methode polymorph überschreibt. Starten zeigt aber in der Klasse Segelflugzeug wegen des Modifikators new kein polymorphes Verhalten mehr.

Das Überschreiben einer mit new überdeckenden Methode mit override ist hingegen nicht möglich, wie das folgende Codefragment zeigt:

public class Flugzeug : Luftfahrzeug {
public new void Starten() { [...] }
}
public class Segelflugzeug : Flugzeug {
public override void Starten () { [...] }
}

Listing 4.22 Fehlerhaftes Überschreiben und Ausblenden in einer Vererbungskette

Ein einmal verloren gegangenes polymorphes Verhalten kann nicht mehr reaktiviert werden.

Zusammenfassende Anmerkungen

Um polymorphes Verhalten einer Methode zu ermöglichen, muss sie in der Basisklasse als virtual definiert sein. Virtuelle Methoden haben immer einen Anweisungsblock und stellen ein Angebot an die ableitenden Klassen dar: Entweder wird die Methode einfach nur geerbt, oder sie wird in der ableitenden Klasse neu implementiert. Zur Umsetzung des zuletzt angeführten Falls gibt es wiederum zwei Möglichkeiten:

  • Wird in der abgeleiteten Klasse die geerbte Methode mit dem Schlüsselwort override implementiert, wird die ursprüngliche Methode überschrieben – die abgeleitete Klasse akzeptiert das Angebot der Basisklasse. Ein Aufruf an eine Referenz der Basisklasse wird polymorph an den sich tatsächlich dahinter verbergenden Typ weitergeleitet.
  • In der abgeleiteten Klasse wird eine virtuelle Methode mit dem Modifizierer new ausgeblendet. Dann verdeckt die Subklassenmethode die geerbte Implementierung der Basisklasse und zeigt kein polymorphes Verhalten.

Eine statische Methode kann nicht virtuell sein. Ebenso ist eine Kombination des Schlüsselworts virtual mit abstract oder override nicht zulässig. Hinter der Definition einer virtuellen Methode verbirgt sich die Absicht, polymorphes Verhalten zu ermöglichen. Daher ergibt es auch keinen Sinn, ein privates Klassenmitglied virtual zu deklarieren – es kommt zu einem Kompilierfehler. new und override schließen sich gegenseitig aus.

Entwickeln Sie eine ableitbare Klasse, sollten Sie grundsätzlich immer an die ableitenden Klassen denken. Polymorphie gehört zu den fundamentalen Prinzipien des objektorientierten Ansatzes. Methoden, die in abgeleiteten Klassen neu implementiert werden müssen, werden vermutlich immer polymorph überschrieben. Vergessen Sie daher die Angabe des Modifizierers virtual in keiner Methode – es sei denn, Sie haben handfeste Gründe, polymorphe Aufrufe bereits im Ansatz zu unterbinden.

Andererseits sollte man sich beim Einsatz von virtual auch darüber bewusst sein, dass die Laufzeitumgebung beim polymorphen Aufruf einer Methode dynamisch nach der typspezifischen Methode suchen muss, was natürlich zu Lasten der Performance geht. Man sollte folglich nicht prinzipiell virtual mit dem Gießkannenprinzip auf alle Methoden verteilen, sondern sich auch über den erwähnten Nachteil im Klaren sein.

Die Methode »ToString()« der Klasse »Object« überschreiben

Die Klasse Object ist die Basis aller .NET-Typen und vererbt jeder Klasse eine Reihe elementarer Methoden. Dazu gehört auch ToString. Diese Methode ist als virtuelle Methode definiert und ermöglicht daher polymorphes Überschreiben. ToString liefert per Vorgabe den kompletten Typbezeichner des aktuellen Objekts als Zeichenfolge an den Aufrufer zurück, wird aber von vielen Klassen des .NET Frameworks überschrieben. Aufgerufen auf einen int liefert ToString beispielsweise den von der int-Variablen beschriebenen Wert als Zeichenfolge.

Wir wollen das Angebot der Methode ToString wahrnehmen und sie in der Klasse Circle ebenfalls polymorph überschreiben. Der Aufruf der Methode soll dem Aufrufer typspezifische Angaben liefern.

public class Circle {
[...]
public override string ToString() {
return "Circle, R=" + Radius + ",Fläche=" + GetArea();
}
}

Listing 4.23 Überschreiben der geerbten Methode »Object.ToString()«



Ihre Meinung

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

<< zurück
  Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: Visual C# 2012

Visual C# 2012
Jetzt Buch bestellen


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

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






 Professionell
 entwickeln mit
 Visual C# 2012


Zum Rheinwerk-Shop: Windows Presentation Foundation






 Windows Presentation
 Foundation


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






 Schrödinger
 programmiert C++


Zum Rheinwerk-Shop: C++ Handbuch






 C++ Handbuch


Zum Rheinwerk-Shop: C/C++






 C/C++


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





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


Nutzungsbestimmungen | Datenschutz | Impressum

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

Cookie-Einstellungen ändern