Menu
Coddy logo textTech

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...");
    }
}

Beachte, dass sealed bei einer Methode mit override kombiniert werden muss. Du kannst nur eine Methode versiegeln, die in einer übergeordneten Klasse bereits virtuell war. Dadurch erhältst du präzise Kontrolle: Erlaube eine gewisse Anpassung in der Vererbungshierarchie und schränke sie dann an einem bestimmten Punkt ein.

challenge icon

Aufgabe

Einfach

Erstellen wir ein Zahlungsabwicklungssystem, das zeigt, wie man sealed verwendet, um kritische Implementierungen abzusichern. Du erstellst eine Hierarchie, in der bestimmte Zahlungsmethoden erweitert werden können, während die sichersten versiegelt sind, um Änderungen zu verhindern.

Du erstellst vier Dateien, um deinen Code zu organisieren:

  • PaymentMethod.cs: Definiere eine PaymentMethod-Klasse im Namespace Payments. Diese Basisklasse verfügt über eine Name-Eigenschaft (string) und einen Konstruktor, der sie festlegt. Füge eine virtual-Methode namens ProcessPayment(decimal amount) hinzu, die "Processing ${amount} via {Name}" zurückgibt.
  • CreditCardPayment.cs: Definiere eine CreditCardPayment-Klasse im Namespace Payments, die von PaymentMethod erbt. Füge eine CardNumber-Eigenschaft (string) hinzu. Der Konstruktor akzeptiert den Namen und die Kartennummer und verwendet base, um den Namen zu übergeben. Überschreibe ProcessPayment mit dem Schlüsselwort sealed, sodass keine weiteren abgeleiteten Klassen dieses Verhalten ändern können. Die Methode soll "Charging ${amount} to card ending in {last 4 digits of CardNumber}" zurückgeben.
  • BankTransfer.cs: Definiere eine sealed-Klasse namens BankTransfer im Namespace Payments, die von PaymentMethod erbt. Die gesamte Klasse ist versiegelt – niemand kann von ihr erben. Füge eine BankName-Eigenschaft (string) hinzu. Der Konstruktor akzeptiert den Namen und den Banknamen. Überschreibe ProcessPayment, sodass "Transferring ${amount} from {BankName}" zurückgegeben wird.
  • Program.cs: Erstelle in deiner Hauptdatei Instanzen aller drei Zahlungstypen mithilfe von Eingabewerten. Speichere sie als PaymentMethod-Referenzen und rufe für jede Instanz ProcessPayment auf, um Polymorphismus mit versiegelten Implementierungen zu demonstrieren.

Du erhältst vier Eingaben:

  • Kreditkartennummer (16 Ziffern)
  • Bankname
  • Zahlungsbetrag

Gib das Ergebnis der Zahlungsabwicklung über jede Methode aus:

{base PaymentMethod result}
{CreditCardPayment result}
{BankTransfer result}

Wenn die Eingaben beispielsweise 1234567890123456, First National und 150.00 lauten, sollte die Ausgabe folgendermaßen aussehen:

Processing $150.00 via Generic
Charging $150.00 to card ending in 3456
Transferring $150.00 from First National

Verwende "Generic" als Namen für die PaymentMethod-Instanz der Basisklasse, "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, nicht ändern kann, wie Kreditkartenzahlungen verarbeitet werden. Die versiegelte Klasse BankTransfer verhindert jegliche Vererbung – beide sind wichtige Sicherheitsmuster in Zahlungssystemen!

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
    }
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-C#-Compiler