Virtual & Override Schlüsselwörter
Teil des Abschnitts Objektorientierte Programmierung der C#-Journey von Coddy — Lektion 21 von 70.
Wenn eine abgeleitete Klasse eine Methode erbt, möchten Sie manchmal das Verhalten dieser Methode vollständig ändern. Die Schlüsselwörter virtual und override arbeiten zusammen, um dies auf kontrollierte Weise zu ermöglichen.
Markieren Sie eine Methode als virtual in der Basisklasse, um zu signalisieren, dass abgeleitete Klassen deren Implementierung ersetzen dürfen:
public class Animal
{
public virtual void Speak()
{
Console.WriteLine("Some sound");
}
}
public class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("Woof!");
}
}Das Schlüsselwort override in der abgeleiteten Klasse teilt dem Compiler mit, dass Sie die Basis-Methode absichtlich ersetzen. Ohne virtual in der Basis-Methode können Sie override nicht verwenden.
Die wahre Stärke zeigt sich, wenn Sie eine Basisklassenreferenz verwenden, um ein abgeleitetes Objekt zu halten:
Animal myPet = new Dog();
myPet.Speak(); // Ausgabe: Woof!Obwohl myPet als Animal deklariert ist, ruft C# die Dog-Version von Speak() auf. Dies wird als Laufzeitpolymorphismus bezeichnet – die tatsächlich aufgerufene Methode hängt vom tatsächlichen Typ des Objekts ab, nicht vom deklarierten Typ der Variablen.
Sie können weiterhin auf die Basisimplementierung zugreifen, indem Sie base.Speak() innerhalb Ihres Overrides verwenden, wenn Sie das Verhalten erweitern möchten, anstatt es vollständig zu ersetzen.
Aufgabe
EinfachLassen Sie uns ein Benachrichtigungssystem erstellen, das demonstriert, wie abgeleitete Klassen das Verhalten der Basisklasse mithilfe der Schlüsselwörter virtual und override vollständig überschreiben können, während gleichzeitig die Leistungsfähigkeit der Laufzeitpolymorphie gezeigt wird.
Sie werden drei Dateien erstellen, um Ihren Code zu organisieren:
Notification.cs: Definieren Sie eineNotification-Klasse im NamespaceAlerts. Diese Basisklasse repräsentiert eine generische Benachrichtigung mit einerMessage-Eigenschaft (String) und einem Konstruktor, der diese setzt. Fügen Sie einevirtualMethode namensSend()hinzu, die"Sending notification: {Message}"zurückgibt.EmailNotification.cs: Definieren Sie eineEmailNotification-Klasse im NamespaceAlerts, die vonNotificationerbt. Fügen Sie eineRecipient-Eigenschaft (String) und einen Konstruktor hinzu, der sowohl die Nachricht als auch den Empfänger akzeptiert (verwenden Siebase, um die Nachricht zu übergeben). Überschreiben Sie die MethodeSend(), sodass sie"Emailing {Recipient}: {Message}"zurückgibt.SmsNotification.cs: Definieren Sie eineSmsNotification-Klasse im NamespaceAlerts, die ebenfalls vonNotificationerbt. Fügen Sie einePhoneNumber-Eigenschaft (String) und einen Konstruktor hinzu, der sowohl die Nachricht als auch die Telefonnummer akzeptiert. Überschreiben SieSend(), um"SMS to {PhoneNumber}: {Message}"zurückzugeben.Program.cs: Erstellen Sie in Ihrer Hauptdatei Instanzen aller drei Benachrichtigungstypen unter Verwendung von Eingabewerten. Demonstrieren Sie dann die Laufzeitpolymorphie, indem Sie alle drei inNotification-Variablen speichern undSend()für jede einzelne aufrufen.
Sie erhalten vier Eingaben:
- Die Benachrichtigungsnachricht
- Die E-Mail-Empfängeradresse
- Die Telefonnummer für SMS
Geben Sie das Ergebnis des Aufrufs von Send() für jede Benachrichtigung aus, die als Basisklassenreferenz gespeichert ist:
{base notification Send() result}
{email notification Send() result}
{sms notification Send() result}Wenn die Eingaben beispielsweise Meeting at 3pm, alice@email.com und 555-1234 sind, sollte die Ausgabe wie folgt aussehen:
Sending notification: Meeting at 3pm
Emailing alice@email.com: Meeting at 3pm
SMS to 555-1234: Meeting at 3pmBeachten Sie, dass, obwohl alle drei als Notification-Variablen gespeichert sind, jede ihre eigene überschriebene Version von Send() aufruft. Das ist Laufzeitpolymorphie in Aktion!
Spickzettel
Verwenden Sie das Schlüsselwort virtual in einer Basisklasse, um eine Methode zu markieren, die von abgeleiteten Klassen überschrieben werden kann:
public class Animal
{
public virtual void Speak()
{
Console.WriteLine("Some sound");
}
}Verwenden Sie das Schlüsselwort override in einer abgeleiteten Klasse, um die Implementierung der Basisklassenmethode zu ersetzen:
public class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("Woof!");
}
}Laufzeitpolymorphismus tritt auf, wenn eine Basisklassenreferenz ein abgeleitetes Objekt enthält – die tatsächlich aufgerufene Methode hängt vom tatsächlichen Typ des Objekts ab, nicht vom deklarierten Typ der Variablen:
Animal myPet = new Dog();
myPet.Speak(); // Ausgabe: Woof!Sie können auf die Basisimplementierung innerhalb eines Overrides mit base zugreifen:
public override void Speak()
{
base.Speak(); // Ruft die Version der Basisklasse auf
Console.WriteLine("Woof!");
}Probier es selbst
using System;
using Alerts;
class Program
{
public static void Main(string[] args)
{
// Eingaben lesen
string message = Console.ReadLine();
string email = Console.ReadLine();
string phone = Console.ReadLine();
// TODO: Instanzen aller drei Benachrichtigungstypen erstellen
// Speichere sie in Notification Variablen, um Polymorphie zu demonstrieren
// TODO: Eine Basis-Notification erstellen
Notification baseNotification;
// TODO: Eine EmailNotification erstellen (gespeichert als Notification Typ)
Notification emailNotification;
// TODO: Eine SmsNotification erstellen (gespeichert als Notification Typ)
Notification smsNotification;
// TODO: Send() für jede aufrufen und die Ergebnisse ausgeben
// Die überschriebenen Methoden werden aufgrund von Laufzeitpolymorphie aufgerufen
}
}
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