Compile- vs. Laufzeit-Polymorphie
Teil des Abschnitts Objektorientierte Programmierung der C#-Journey von Coddy. Lektion 25 von 70.
Polymorphismus bedeutet „viele Formen“ – die Fähigkeit desselben Codes, sich je nach Kontext unterschiedlich zu verhalten. C# unterstützt zwei verschiedene Arten von Polymorphismus, und das Verständnis darüber, wann jede davon aufgelöst wird, hilft Ihnen dabei, flexibleren Code zu schreiben.
Kompilierzeit-Polymorphismus (auch statische Polymorphie genannt) wird vom Compiler aufgelöst, bevor Ihr Programm ausgeführt wird. Methodenüberladung ist das primäre Beispiel – mehrere Methoden teilen sich denselben Namen, haben aber unterschiedliche Parameter:
public class Calculator
{
public int Add(int a, int b) => a + b;
public double Add(double a, double b) => a + b;
}
var calc = new Calculator();
calc.Add(5, 3); // Compiler wählt die int-Version
calc.Add(5.0, 3.0); // Compiler wählt die double-VersionDer Compiler prüft die Argumenttypen und wählt die korrekte Methode zur Kompilierzeit aus. Diese Entscheidung steht fest, bevor das Programm jemals ausgeführt wird.
Laufzeitpolymorphismus (dynamische Polymorphie) wird aufgelöst, während das Programm läuft. Dies geschieht mit virtual und override Methoden – der tatsächliche Typ des Objekts bestimmt, welche Methode ausgeführt wird:
Animal pet = new Dog(); // Als Animal deklariert, tatsächlich ein Dog
pet.Speak(); // Ruft Speak() von Dog zur Laufzeit aufDer Compiler weiß nicht, welches Speak() ausgeführt wird – diese Entscheidung fällt zur Laufzeit basierend auf dem tatsächlichen Typ des Objekts. Das ist es, was vererbungsbasierte Polymorphie so mächtig macht, um flexible, erweiterbare Systeme zu entwickeln.
Aufgabe
EinfachLass uns ein Konvertersystem erstellen, das beide Arten von Polymorphie in Aktion demonstriert. Du erstellst eine Klasse, die Methodenüberladung (Polymorphie zur Kompilierzeit) zusammen mit einer Vererbungshierarchie verwendet, die virtuelle/überschriebene Methoden nutzt (Polymorphie zur Laufzeit).
Du organisierst deinen Code auf vier Dateien verteilt:
Converter.cs: Definiere eineConverter-Klasse im NamespaceConversion. Diese Klasse demonstriert Polymorphie zur Kompilierzeit durch Methodenüberladung. Erstelle drei überladeneConvert-Methoden:Convert(int value)gibt"Integer: {value}"zurückConvert(double value)gibt"Double: {value}"zurückConvert(string value)gibt"String: {value}"zurück
Shape.cs: Definiere eine BasisklasseShapeim NamespaceConversion. Diese Klasse besitzt eineName-Eigenschaft (string) und einen Konstruktor, der sie festlegt. Füge einevirtual-Methode namensDescribe()hinzu, die"This is a {Name}"zurückgibt.Circle.cs: Definiere eineCircle-Klasse im NamespaceConversion, die vonShapeerbt. Füge eineRadius-Eigenschaft (double) hinzu. Der Konstruktor akzeptiert einen Radius und übergibt"Circle"an den Basiskonstruktor. ÜberschreibeDescribe(), sodass"This is a Circle with radius {Radius}"zurückgegeben wird.Program.cs: Demonstriere in deiner Hauptdatei beide Arten von Polymorphie. Erstelle zuerst einenConverterund rufe alle drei überladenen Methoden mit Eingabewerten auf. Erstelle anschließend einenCircle, speichere ihn in einerShape-Variablen und rufeDescribe()auf, um die Polymorphie zur Laufzeit zu zeigen.
Du erhältst vier Eingaben:
- Einen ganzzahligen Wert
- Einen double-Wert
- Einen Zeichenfolgenwert
- Einen Kreisradius (double)
Gib die Ausgabe in diesem Format aus:
Compile-time Polymorphism:
{Convert(int) result}
{Convert(double) result}
{Convert(string) result}
Runtime Polymorphism:
{Describe() result from Shape variable holding Circle}Wenn die Eingaben beispielsweise 42, 3.14, Hello und 5.5 lauten, sollte die Ausgabe wie folgt aussehen:
Compile-time Polymorphism:
Integer: 42
Double: 3.14
String: Hello
Runtime Polymorphism:
This is a Circle with radius 5.5Beachte den entscheidenden Unterschied: Der Compiler entscheidet zur Kompilierzeit anhand der Argumenttypen, welche Convert-Methode aufgerufen wird, während die Describe()-Methode zur Laufzeit anhand des tatsächlichen Objekttyps aufgelöst wird, obwohl sie in einer Shape-Variablen gespeichert ist!
Probier es selbst
using System;
using Conversion;
class Program
{
public static void Main(string[] args)
{
// Eingaben lesen
int intValue = Convert.ToInt32(Console.ReadLine());
double doubleValue = Convert.ToDouble(Console.ReadLine());
string stringValue = Console.ReadLine();
double radius = Convert.ToDouble(Console.ReadLine());
// TODO: Kompilierzeit-Polymorphismus demonstrieren
// Erstellen Sie ein Converter-Objekt und rufen Sie alle drei überladenen Convert-Methoden auf
Console.WriteLine("Compile-time Polymorphism:");
// Rufen Sie Convert mit intValue, doubleValue und stringValue auf
// TODO: Laufzeit-Polymorphismus demonstrieren
// Erstellen Sie ein Circle-Objekt und speichern Sie es in einer Shape-Variable
// Rufen Sie dann Describe() auf der Shape-Variable auf
Console.WriteLine("Runtime Polymorphism:");
// Circle erstellen und Describe() aufrufen
}
}
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. EigenschaftenKonstruktorenObjektinitialisiererWiederholung – Einfacher Taschenrechner4Vererbung
Grundlegende Vererbungs-(:)-SyntaxDas „base“-Schlüsselwort„virtual“- und „override“-SchlüsselwörterVersiegelte KlassenDie „object“-BasisklasseWiederholung – Mitarbeiterhierarchie7Erweiterte Funktionen
Überladen von OperatorenIndexer (this[])ToString()-ÜberschreibungErweiterungsmethodenRückblick – Benutzerdefinierte Liste10Entwurfsmuster Teil 1
Einführung in EntwurfsmusterThread-sicheres SingletonFactory-MusterObserver-Muster (Events)Strategy-Muster13Abschließende Herausforderungen
E-Learning-PlattformKomponente für SpielfigurenFahrzeugvermietungsdienst2Eigenschaften & statische Member
Automatisch implementierte EigenschaftenNur lesbare/schreibbare EigenschaftenStatische Felder & MethodenStatische KlassenMember mit Ausdruckskörpern5Polymorphie & Interfaces
Compile- vs. Laufzeit-PolymorphieInterface vs. abstrakte KlasseMehrere InterfacesExplizite InterfacesUpcasting & DowncastingWiederholung – Formenrechner3Klassenarchitektur
Instanz- vs. statische DatenSchlüsselwörter 'readonly' & 'const'Backing FieldsRückblick – Bankkontenverwaltung6Kapselung
ZugriffsmodifikatorenEigenschaften zur KapselungImplementierung der DatenkapselungMuster für UnveränderlichkeitRückblick – SchülerdatenÜbe selbstständig: Online-C#-Compiler