Upcasting & Downcasting
Teil des Abschnitts Objektorientierte Programmierung der C#-Journey von Coddy — Lektion 29 von 70.
Beim Arbeiten mit Vererbungshierarchien müssen Sie häufig zwischen Basis- und abgeleiteten Typen konvertieren. Upcasting konvertiert einen abgeleiteten Typ in seinen Basistyp, während Downcasting das Gegenteil bewirkt.
Upcasting ist implizit und immer sicher, da eine abgeleitete Klasse garantiert alles besitzt, was die Basisklasse hat:
Dog myDog = new Dog();
Animal animal = myDog; // Upcasting - implizit, funktioniert immerDowncasting erfordert eine explizite Typumwandlung und kann zur Laufzeit fehlschlagen, wenn das Objekt nicht tatsächlich der Typ ist, in den Sie umwandeln:
Animal animal = new Dog();
Dog dog = (Dog)animal; // Downcasting - funktioniert, weil es wirklich ein Dog ist
Animal cat = new Cat();
Dog wrong = (Dog)cat; // Wirft InvalidCastException!Um sicher ein Downcasting durchzuführen, verwenden Sie das Schlüsselwort is, um zuerst den Typ zu prüfen, oder verwenden Sie as, das null zurückgibt, anstatt eine Exception auszulösen:
if (animal is Dog d)
{
d.Bark(); // Sicher - d ist nur verfügbar, wenn die Prüfung erfolgreich ist
}
Dog maybeDog = animal as Dog;
if (maybeDog != null)
{
maybeDog.Bark();
}Der is-Pattern-Matching-Ansatz wird in modernem C# bevorzugt, da er die Typprüfung und die Typumwandlung in einem Schritt kombiniert, wodurch Ihr Code sowohl sicherer als auch sauberer wird.
Aufgabe
EinfachLassen Sie uns ein Fahrzeug-Inspektionssystem bauen, das demonstriert, wie Upcasting und Downcasting es Ihnen ermöglichen, flexibel mit Objekten in einer Vererbungshierarchie zu arbeiten. Sie werden verschiedene Fahrzeugtypen erstellen und Code schreiben, der spezifische Fahrzeugarten sicher identifiziert und mit ihnen interagiert.
Sie werden Ihren Code in vier Dateien organisieren:
Vehicle.cs: Definieren Sie eine BasisklasseVehicleim NamespaceInspection. Jedes Fahrzeug hat eine EigenschaftLicensePlate(string), die über einen Konstruktor gesetzt wird. Fügen Sie eine Methode namensGetBasicInfo()hinzu, die"Vehicle: {LicensePlate}"zurückgibt.Car.cs: Definieren Sie eine KlasseCarim NamespaceInspection, die vonVehicleerbt. Autos haben eine zusätzliche EigenschaftNumberOfDoors(int). Der Konstruktor akzeptiert sowohl das Kennzeichen als auch die Anzahl der Türen. Fügen Sie eine Methode namensHonk()hinzu, die"{LicensePlate} honks: Beep beep!"zurückgibt.Motorcycle.cs: Definieren Sie eine KlasseMotorcycleim NamespaceInspection, die vonVehicleerbt. Motorräder haben eine EigenschaftHasSidecar(bool). Der Konstruktor akzeptiert das Kennzeichen und den Beiwagen-Status. Fügen Sie eine Methode namensRev()hinzu, die"{LicensePlate} revs: Vroom!"zurückgibt.Program.cs: In Ihrer Hauptdatei erstellen Sie Fahrzeuge und üben sowohl Upcasting als auch sicheres Downcasting. Erstellen Sie einCarund einMotorcycleunter Verwendung von Eingabewerten und speichern Sie beide in einem Array vonVehicle-Referenzen (Upcasting). Durchlaufen Sie das Array in einer Schleife und geben Sie für jedes Fahrzeug seine Basisinformationen aus. Verwenden Sie dann das Schlüsselwortismit Pattern Matching für ein sicheres Downcasting: Wenn es einCarist, rufen SieHonk()auf; wenn es einMotorcycleist, rufen SieRev()auf.
Sie erhalten vier Eingaben:
- Auto-Kennzeichen
- Anzahl der Türen
- Motorrad-Kennzeichen
- Hat Beiwagen (
trueoderfalse)
Geben Sie die Ausgabe für jedes Fahrzeug im Array in diesem Format aus:
{GetBasicInfo() result}
{Honk() or Rev() result based on actual type}Wenn die Eingaben beispielsweise ABC-123, 4, XYZ-789 und false sind, sollte die Ausgabe wie folgt aussehen:
Vehicle: ABC-123
ABC-123 honks: Beep beep!
Vehicle: XYZ-789
XYZ-789 revs: Vroom!Beachten Sie, wie alle Fahrzeuge als Vehicle-Referenzen gespeichert werden (Upcasting), Sie aber ihre tatsächlichen Typen sicher entdecken und auf typspezifische Methoden zugreifen können, indem Sie die is-Pattern-Matching-Syntax für das Downcasting verwenden!
Spickzettel
Beim Arbeiten mit Vererbungshierarchien konvertiert Upcasting einen abgeleiteten Typ in seinen Basistyp, während Downcasting einen Basistyp in einen abgeleiteten Typ konvertiert.
Upcasting erfolgt implizit und ist immer sicher:
Dog myDog = new Dog();
Animal animal = myDog; // Upcasting - implicit, always worksDowncasting erfordert eine explizite Typumwandlung (Cast) und kann zur Laufzeit fehlschlagen:
Animal animal = new Dog();
Dog dog = (Dog)animal; // Downcasting - works because it's really a Dog
Animal cat = new Cat();
Dog wrong = (Dog)cat; // Throws InvalidCastException!Sicheres Downcasting unter Verwendung des is-Schlüsselworts mit Pattern Matching (bevorzugt):
if (animal is Dog d)
{
d.Bark(); // Safe - d is only available if check passes
}Sicheres Downcasting unter Verwendung des as-Schlüsselworts (gibt null zurück, wenn die Umwandlung fehlschlägt):
Dog maybeDog = animal as Dog;
if (maybeDog != null)
{
maybeDog.Bark();
}Probier es selbst
using System;
using Inspection;
class Program
{
public static void Main(string[] args)
{
// Eingaben lesen
string carPlate = Console.ReadLine();
int numberOfDoors = Convert.ToInt32(Console.ReadLine());
string motorcyclePlate = Console.ReadLine();
bool hasSidecar = Convert.ToBoolean(Console.ReadLine());
// TODO: Erstelle ein Car-Objekt unter Verwendung von carPlate und numberOfDoors
// TODO: Erstelle ein Motorcycle-Objekt unter Verwendung von motorcyclePlate und hasSidecar
// TODO: Erstelle ein Array von Vehicle-Referenzen, das beide Fahrzeuge enthält (Upcasting)
// TODO: Durchlaufe das Array in einer Schleife und für jedes Fahrzeug:
// - Gib das Ergebnis von GetBasicInfo() aus
// - Verwende das Schlüsselwort 'is' mit Pattern Matching für ein sicheres Downcasting:
// - Wenn es ein Car ist, rufe Honk() auf und gib es aus
// - Wenn es ein Motorcycle ist, rufe Rev() auf und gib es aus
}
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienNamespaces & DirektivenEinführung in Klassen & ObjekteDas 'this'-SchlüsselwortMethoden und ParameterFelder vs. EigenschaftenKonstruktorenObjekt-InitialisiererZusammenfassung - Einfacher Taschenrechner4Vererbung
Grundlagen der Vererbung (:) SyntaxDas 'base'-SchlüsselwortVirtual & Override SchlüsselwörterVersiegelte KlassenDie 'object'-BasisklasseZusammenfassung – Mitarbeiter-Hierarchie7Fortgeschrittene Funktionen
OperatorüberladungIndexer (this[])ToString() überschreibenErweiterungsmethodenZusammenfassung - Benutzerdefinierte Liste2Eigenschaften & Statische Member
Automatisch implementierte EigenschaftenRead-Only- & Write-Only-EigenschaftenStatische Felder & MethodenStatische KlassenExpression-Bodied Member5Polymorphismus & Interfaces
Compile- vs. Runtime-PolymorphismusInterface vs. abstrakte KlasseMehrfache InterfacesExplizite InterfacesUpcasting & DowncastingRecap – Shape Calculator3Klassenarchitektur
Instanz- vs. statische Daten'readonly' & 'const' SchlüsselwörterBacking FieldsZusammenfassung - Bankkonto-Manager6Kapselung
ZugriffsmodifikatorenProperties zur KapselungImplementierung von Data HidingImmutability-MusterRückblick – Studierendendaten