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.2 Der Problemfall geerbter MethodenZur nächsten Überschrift

Um das objektorientierte Konzept zu erläutern, habe ich mich bisher meistens des Beispiels der beiden Klassen Circle und GraphicCircle bedient. Sie haben mit diesen beiden Klassen gelernt, wie die Struktur einer Klasse samt ihren Feldern, Methoden und Konstruktoren aufgebaut ist. Sie wissen nun auch, wie durch die Vererbung eine Klasse automatisch mit Fähigkeiten ausgestattet wird, die sie aus der Basisklasse erbt. Nun werden wir uns einer zweiten Klassenhierarchie zuwenden, um weitere Aspekte der Objektorientierung auf möglichst anschauliche Weise zu erklären.

Ausgangspunkt ist die Klasse Luftfahrzeug, die von den beiden Klassen Flugzeug und Hubschrauber beerbt wird. In der Klasse Luftfahrzeug sind die Felder definiert, die alle davon abgeleiteten Klassen gemeinsam aufweisen: Hersteller und Baujahr. Die Spannweite ist eine Eigenschaft, die nur ein Flugzeug hat, und ist daher in der Klasse Flugzeug implementiert. Ein Hubschrauber wiederum hat demgegenüber einen Rotordurchmesser. Da die abgeleiteten Typen starten können, ist die entsprechende Methode in der Basisklasse Luftfahrzeug implementiert.

Das nachfolgende Codefragment bildet die Situation ab. Dabei enthält die Methode Starten nur »symbolischen« Code.

public class Luftfahrzeug {
public string Hersteller {get; set;}
public int Baujahr {get; set;}
public void Starten() {
Console.WriteLine("Das Luftfahrzeug startet.");
}
}
public class Flugzeug : Luftfahrzeug {
public double Spannweite {get; set;}
}
public class Hubschrauber : Luftfahrzeug {
public double Rotor {get; set;}
}

Listing 4.6 Klassen der Hierarchie der Luftfahrzeuge

In Abbildung 4.4 sehen Sie die Zusammenhänge auf anschauliche Art.

Abbildung

Abbildung 4.4 Die Hierarchie der Luftfahrzeuge

Grundsätzlich scheint die Vererbungshierarchie den Anforderungen zu genügen, aber denken Sie einen Schritt weiter: Ist die Implementierung der Methode Starten in der Basisklasse Luftfahrzeug anforderungsgerecht? Denn im Grunde genommen startet ein Flugzeug anders als ein Hubschrauber – zumindest in den meisten Fällen. Ganz allgemein ausgedrückt stehen wir vor der folgenden Frage: Wie kann eine Methode in der Basisklasse implementiert werden, wenn sich das operative Verhalten in den Methoden der abgeleiteten Klassen unterscheidet? Einfach auf die Bereitstellung der Methode in der Basisklasse zu verzichten, ist definitiv keine Lösung. Denn unsere Absicht sei es, zu garantieren, dass jede abgeleitete Klasse die Methode – in unserem Fall Starten – bereitstellt.

Prinzipiell bieten sich drei Lösungsansätze an:

  • Wir verdecken die geerbten Methoden der Basisklasse in der abgeleiteten Klasse mit dem Modifizierer new.
  • Wir stellen in der Basisklasse abstrakte Methoden bereit, die von den erbenden Klassen überschrieben werden müssen.
  • Wir stellen in der Basisklasse virtuelle Methoden bereit.

Nachfolgend wollen wir alle drei Alternativen genau untersuchen.


Rheinwerk Computing - Zum Seitenanfang

4.2.1 Geerbte Methoden mit »new« verdeckenZur nächsten ÜberschriftZur vorigen Überschrift

Nehmen wir an, dass in der Basisklasse die Methode Starten wie folgt codiert ist:

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

Listing 4.7 Annahme: Implementierung der Methode »Starten« in der Basisklasse

In den beiden abgeleiteten Klassen soll die Methode Starten nunmehr eine typspezifische Implementierung aufweisen. Realisiert wird das durch eine Neuimplementierung der Methode in der abgeleiteten Klasse. Dabei muss die Methode mit dem Modifizierer new signiert werden, um deutlich zu machen, dass es sich um eine beabsichtigte Neuimplementierung handelt und nicht um einen unbeabsichtigten Fehler. Man spricht bei dieser Vorgehensweise auch vom Ausblenden oder Verdecken einer geerbten Methode.

Exemplarisch sei das an der Klasse Flugzeug gezeigt, gilt aber natürlich in gleicher Weise auch für den Typ Hubschrauber:

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

Listing 4.8 Verdecken der geerbten Methode mit dem Modifikator »new«

Vom Verdecken oder Ausblenden einer geerbten Basisklassenmethode wird gesprochen, wenn in der abgeleiteten Klasse eine Methode implementiert wird,

  • die den gleichen Namen und
  • eine identische Parameterliste

besitzt wie eine Methode in der Basisklasse, diese aber durch eine eigene Implementierung vollständig ersetzt. Das ist beispielsweise der Fall, wenn die Implementierung in der Basisklasse für Objekte vom Typ der abgeleiteten Klasse falsch ist oder generell anders sein muss. Entscheidend für das Verdecken einer geerbten Methode ist die Ergänzung der Methodendefinition in der Subklasse um den Modifizierer new.

Wird eine Basisklassenmethode in der abgeleiteten Klasse verdeckt, wird beim Aufruf der Methode auf Objekte vom Typ der Subklasse immer die verdeckende Version ausgeführt. Zum Testen in Main schreiben wir den folgenden Code:

Flugzeug flg = new Flugzeug();
flg.Starten();
Hubschrauber hubi = new Hubschrauber();
hubi.Starten();

Listing 4.9 Testen der Methode »Starten«

Im Befehlsfenster kommt es zu den Ausgaben »Das Flugzeug startet.« und »Der Hubschrauber startet.«.

Sie finden das Beispiel auf der Buch-DVD unter ..\Beispiele\Kapitel 4\Aircrafts\Sample1.

Statische Member überdecken

In gleicher Weise, wie eine geerbte Instanzmethode in einer ableitenden Klasse verdeckt werden kann, lassen sich mit new auch Eigenschaftsmethoden und statische Komponenten einer Basisklasse verdecken und durch eine typspezifische Implementierung ersetzen. Die in den folgenden Abschnitten noch zu behandelnden Modifizierer abstract, virtual und override sind im Zusammenhang mit statischen Membern nicht erlaubt.


Rheinwerk Computing - Zum Seitenanfang

4.2.2 Abstrakte MethodenZur nächsten ÜberschriftZur vorigen Überschrift

Mit dem Modifizierer new können die aus der Basisklasse geerbten Methoden in der ableitenden Klasse überdeckt werden. Allerdings ist dieser Lösungsweg mit einem Nachteil behaftet, denn er garantiert nicht, dass alle ableitenden Klassen die geerbte Methode Starten durch eine typspezifische Implementierung ersetzen. Jede unserer abgeleiteten Klassen sollte aber hinsichtlich der Behandlung einer Basisklassenoperation gleichwertig sein. Wird die Neuimplementierung beispielsweise in der Klasse Hubschrauber vergessen, ist dieser Typ mit einem möglicherweise entscheidenden Fehler behaftet, weil er keine typspezifische Neuimplementierung hat.

Wie können wir aber alle Typen, die die Klasse Luftfahrzeug ableiten, dazu zwingen, die Methode Starten neu zu implementieren? Gehen wir noch einen Schritt weiter und stellen wir uns die Frage, ob wir dann überhaupt noch Code in der Methode Starten der Klasse Luftfahrzeug benötigen. Anscheinend nicht! Dass wir die Methode in der Basisklasse definiert haben, liegt im Grunde genommen nur daran, dass wir die Methode Starten in allen ableitenden Klassen bereitstellen wollen.

Mit dieser Erkenntnis mag die Lösung der aufgezeigten Problematik im ersten Moment verblüffen: Tatsächlich wird Starten in der Basisklasse nicht implementiert – sie bleibt einfach ohne Programmcode. Damit wäre aber noch nicht sichergestellt, dass die ableitenden Klassen die geerbte »leere« Methode typspezifisch implementieren. Deshalb wird in solchen Fällen sogar auf den Anweisungsblock verzichtet, der durch die geschweiften Klammern beschrieben wird.

In der objektorientierten Programmierung werden Methoden, die keinen Anweisungsblock haben, als abstrakte Methoden bezeichnet. Neben den Methoden, die das Verhalten eines Typs beschreiben, können auch Eigenschaften abstrakt definiert werden.

Abstrakte Methoden werden durch die Angabe des abstract-Modifizierers in der Methodensignatur gekennzeichnet, am Beispiel unserer Methode Starten also durch:

public abstract void Starten();

Abstrakte Methoden enthalten niemals Code. Die Definition einer abstrakten Methode wird mit einem Semikolon direkt hinter der Parameterliste abgeschlossen, die geschweiften Klammern des Anweisungsblocks entfallen.

Welchen Stellenwert nimmt aber eine Klasse ein, die eine Methode veröffentlicht, die keinerlei Verhalten aufweist? Die Antwort ist verblüffend einfach: Eine solche Klasse kann nicht instanziiert werden – sie rechtfertigt ihre Existenz einzig und allein dadurch, den abgeleiteten Klassen als Methodenbereitsteller zu dienen. Damit wird das Prinzip der objektorientierten Programmierung, gemeinsame Verhaltensweisen auf eine höhere Ebene auszulagern, nahezu auf die Spitze getrieben.

Eine nicht instanziierbare Klasse, die mindestens einen durch abstract gekennzeichneten Member enthält, ist ihrerseits selbst abstrakt und wird deshalb als abstrakte Klasse bezeichnet. Abstrakte Klassen sind nur dann sinnvoll, wenn sie abgeleitet werden. Syntaktisch wird dieses Verhalten in C# durch die Ergänzung des Modifikators abstract in der Klassensignatur beschrieben:

public abstract class Luftfahrzeug {
public abstract void Starten();
[...]
}

Listing 4.10 Abstrakte Definition der Methode »Starten«

Neben abstrakten Methoden darf eine abstrakte Klasse auch vollständig implementierte, also nichtabstrakte Methoden und Eigenschaften bereitstellen.

Die Signatur einer Methode und infolgedessen auch der dazugehörigen Klasse mit dem Modifizierer abstract kommt einer Forderung gleich: Alle nichtabstrakten Ableitungen einer abstrakten Klasse müssen die abstrakten Methoden der Basisklasse überschreiben. Wird in einer abgeleiteten Klasse das abstrakte Mitglied der Basisklasse nicht überschrieben, muss die abgeleitete Klasse ebenfalls abstract gekennzeichnet werden. Als Konsequenz dieser Aussagen bilden abstrakte Klassen das Gegenkonstrukt zu den Klassen, die mit sealed als nicht ableitbar gekennzeichnet sind. Daraus folgt auch, dass die Modifizierer sealed und abstract nicht nebeneinander verwendet werden dürfen.

Eine Klasse, die eine abstrakt definierte Methode enthält, muss ihrerseits selbst abstrakt sein. Der Umkehrschluss ist allerdings nicht richtig, denn eine abstrakte Klasse ist nicht zwangsläufig dadurch gekennzeichnet, mindestens ein abstraktes Mitglied zu enthalten. Eine Klasse kann auch dann abstrakt sein, wenn keiner ihrer Member abstrakt ist. Auf diese Weise wird eine Klasse nicht instanziierbar und das Ableiten dieser Klasse erzwungen.

abstract kann nur im Zusammenhang mit Instanzmembern benutzt werden. Statische Methoden können nicht abstrakt sein, deshalb ist das gleichzeitige Auftreten von static und abstract in einer Methodensignatur unzulässig.

Abstrakte Methoden überschreiben

Das folgende Codefragment beschreibt die Klasse Hubschrauber. In der Klassenimplementierung wird die abstrakte Methode Starten der Basisklasse überschrieben. Zur Kennzeichnung des Überschreibens einer abstrakten Basisklassenmethode dient der Modifizierer override:

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

Listing 4.11 Überschreiben der geerbten abstrakten Methode

Sollten Sie dieses Beispiel ausprobieren, müssen Sie Starten selbstverständlich auch in der Klasse Flugzeug mit override überschreiben.

Sie finden das komplette Beispiel auf der Buch-DVD unter ..\Beispiele\Kapitel 4\Aircrafts\ Sample2.


Rheinwerk Computing - Zum Seitenanfang

4.2.3 Virtuelle MethodenZur vorigen Überschrift

Widmen wir uns nun der dritten anfangs aufgezeigten Variante, den virtuellen Methoden. Ausgangspunkt sei dabei folgender: Wir wollen Starten in der Basisklasse vollständig implementieren. Damit wären wir wieder am Ausgangspunkt angelangt mit einem kleinen Unterschied: Wir ergänzen die Methoden Starten mit dem Modifizierer virtual. Dann sieht die Klasse Luftfahrzeug wie folgt aus:

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

Listing 4.12 Virtuelle Definition der Methode »Starten«

Nun ist die Methode virtuell in der Basisklasse definiert. Eine ableitende Klasse hat nun die Wahl zwischen drei Alternativen:

  • Die ableitende Klasse erbt die Methode, ohne eine eigene, typspezifische Implementierung vorzusehen, also:
    public class Flugzeug : Luftfahrzeug { }
  • Die ableitende Klasse verdeckt die geerbte Methode mit new, hier also:
    public class Flugzeug : Luftfahrzeug {
    public new void Starten() {
    Console.WriteLine("Das Flugzeug startet.");
    }
    }
  • Die ableitende Klasse überschreibt die geerbte Methode mit override, also
    public class Flugzeug : Luftfahrzeug {
    public override void Starten() {
    Console.WriteLine("Das Flugzeug startet.");
    }
    }

Sie finden das komplette Beispiel auf der Buch-DVD unter ..\Beispiele\Kapitel 4\Aircrafts\ Sample3.

Sie werden sich an dieser Stelle wahrscheinlich fragen, worin sich die beiden letztgenannten Varianten unterscheiden. Diese Überlegung führt uns nach der Datenkapselung und der Vererbung zum dritten elementaren Konzept der Objektorientierung: der Polymorphie. Ehe wir uns aber mit der Polymorphie beschäftigen, müssen wir vorher noch die Typumwandlung in einer Vererbungshierarchie verstehen.



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