Versiegelte Klassen
Teil des Abschnitts Objektorientierte Programmierung der C#-Journey von Coddy — Lektion 22 von 70.
Manchmal möchte man die Vererbungskette unterbrechen. Das Schlüsselwort sealed verhindert, dass eine Klasse geerbt wird, wodurch ihre Implementierung gesperrt wird, sodass keine andere Klasse sie erweitern kann.
public sealed class FinalDog : Animal
{
public override void Speak()
{
Console.WriteLine("Woof!");
}
}
// Dies würde zu einem Kompilierfehler führen:
// public class SuperDog : FinalDog { }Warum sollte man eine Klasse versiegeln? Sicherheit und Designintegrität sind häufige Gründe. Wenn Sie eine Klasse sorgfältig entworfen haben und nicht möchten, dass jemand ihr Verhalten durch Vererbung ändert, garantiert das Versiegeln, dass Ihre Implementierung intakt bleibt.
Sie können auch einzelne Methoden versiegeln, anstatt ganze Klassen. Dies verhindert weiteres Überschreiben, während die Klasse selbst weiterhin vererbt werden kann:
public class Dog : Animal
{
public sealed override void Speak()
{
Console.WriteLine("Woof!");
}
}
public class Puppy : Dog
{
// Speak() kann nicht überschrieben werden - es ist versiegelt (sealed)
// Aber es können neue Methoden hinzugefügt werden
public void Whimper()
{
Console.WriteLine("Whimper...");
}
}Beachten Sie, dass sealed bei einer Methode mit override kombiniert werden muss. Sie können nur eine Methode versiegeln, die in einer Basisklasse bereits virtual war. Dies gibt Ihnen eine präzise Kontrolle: Erlauben Sie einige Anpassungen in der Vererbungshierarchie und sperren Sie diese dann an einem bestimmten Punkt ab.
Aufgabe
EinfachLassen Sie uns ein Zahlungssystem entwickeln, das demonstriert, wie man sealed verwendet, um kritische Implementierungen abzusichern. Sie werden eine Hierarchie erstellen, in der bestimmte Zahlungsmethoden erweitert werden können, aber die sichersten versiegelt sind, um jegliche Änderungen zu verhindern.
Sie werden vier Dateien erstellen, um Ihren Code zu organisieren:
PaymentMethod.cs: Definieren Sie eine KlassePaymentMethodim NamespacePayments. Diese Basisklasse hat eine EigenschaftName(string) und einen Konstruktor, der diese setzt. Fügen Sie einevirtualMethode namensProcessPayment(decimal amount)hinzu, die"Processing ${amount} via {Name}"zurückgibt.CreditCardPayment.cs: Definieren Sie eine KlasseCreditCardPaymentim NamespacePayments, die vonPaymentMethoderbt. Fügen Sie eine EigenschaftCardNumber(string) hinzu. Der Konstruktor akzeptiert Name und Kartennummer und verwendetbase, um den Namen zu übergeben. Überschreiben SieProcessPaymentmit dem Schlüsselwortsealed, damit keine weiteren abgeleiteten Klassen dieses Verhalten ändern können. Sie sollte"Charging ${amount} to card ending in {last 4 digits of CardNumber}"zurückgeben.BankTransfer.cs: Definieren Sie einesealedKlasse namensBankTransferim NamespacePayments, die vonPaymentMethoderbt. Diese gesamte Klasse ist versiegelt – niemand kann von ihr erben. Fügen Sie eine EigenschaftBankName(string) hinzu. Der Konstruktor akzeptiert Name und Bankname. Überschreiben SieProcessPayment, um"Transferring ${amount} from {BankName}"zurückzugeben.Program.cs: Erstellen Sie in Ihrer Hauptdatei Instanzen aller drei Zahlungstypen unter Verwendung von Eingabewerten. Speichern Sie diese alsPaymentMethod-Referenzen und rufen SieProcessPaymentfür jede auf, um Polymorphie mit versiegelten Implementierungen zu demonstrieren.
Sie erhalten vier Eingaben:
- Kreditkartennummer (16 Stellen)
- Bankname
- Zahlungsbetrag
Geben Sie das Ergebnis der Zahlungsverarbeitung für jede Methode aus:
{base PaymentMethod result}
{CreditCardPayment result}
{BankTransfer result}Wenn die Eingaben beispielsweise 1234567890123456, First National und 150.00 sind, sollte die Ausgabe wie folgt aussehen:
Processing $150.00 via Generic
Charging $150.00 to card ending in 3456
Transferring $150.00 from First NationalVerwenden Sie "Generic" als Namen für die Basisinstanz von PaymentMethod, "Credit Card" für die Kreditkartenzahlung und "Bank Transfer" für die Banküberweisung.
Die versiegelte Methode in CreditCardPayment stellt sicher, dass jemand, der eine Unterklasse erstellt, die Art und Weise, wie Kreditkartenzahlungen verarbeitet werden, nicht ändern kann. Die versiegelte Klasse BankTransfer verhindert jegliche Vererbung – beides sind wichtige Sicherheitsmuster in Zahlungssystemen!
Spickzettel
Das Schlüsselwort sealed verhindert, dass eine Klasse vererbt wird oder eine Methode in der Vererbungskette weiter überschrieben wird.
Eine Klasse versiegeln:
public sealed class FinalDog : Animal
{
public override void Speak()
{
Console.WriteLine("Woof!");
}
}
// Dies würde einen Kompilierfehler verursachen:
// public class SuperDog : FinalDog { }Versiegelte Klassen werden für die Sicherheit und Designintegrität verwendet, um sicherzustellen, dass die Implementierung nicht durch Vererbung geändert werden kann.
Eine Methode versiegeln:
public class Dog : Animal
{
public sealed override void Speak()
{
Console.WriteLine("Woof!");
}
}
public class Puppy : Dog
{
// Speak() kann nicht überschrieben werden - sie ist versiegelt (sealed)
// Aber es können neue Methoden hinzugefügt werden
public void Whimper()
{
Console.WriteLine("Whimper...");
}
}Das Schlüsselwort sealed bei einer Methode muss mit override kombiniert werden. Sie können nur Methoden versiegeln, die in einer Elternklasse bereits als virtual deklariert waren. Dies ermöglicht Anpassungen bis zu einem bestimmten Punkt in der Hierarchie und sperrt sie dann ab.
Probier es selbst
using System;
using Payments;
class Program
{
public static void Main(string[] args)
{
// Eingaben lesen
string cardNumber = Console.ReadLine();
string bankName = Console.ReadLine();
decimal amount = Convert.ToDecimal(Console.ReadLine());
// TODO: Erstelle eine Basis-PaymentMethod-Instanz mit dem Namen "Generic"
// Speichere sie als PaymentMethod-Referenz
// TODO: Erstelle eine CreditCardPayment-Instanz mit dem Namen "Credit Card" und der Kartennummer
// Speichere sie als PaymentMethod-Referenz
// TODO: Erstelle eine BankTransfer-Instanz mit dem Namen "Bank Transfer" und dem Banknamen
// Speichere sie als PaymentMethod-Referenz
// TODO: Rufe ProcessPayment für jedes Objekt auf und gib die Ergebnisse aus
// Jedes Ergebnis sollte in einer eigenen Zeile stehen
}
}
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