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

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.

challenge icon

Aufgabe

Einfach

Lassen 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 Klasse PaymentMethod im Namespace Payments. Diese Basisklasse hat eine Eigenschaft Name (string) und einen Konstruktor, der diese setzt. Fügen Sie eine virtual Methode namens ProcessPayment(decimal amount) hinzu, die "Processing ${amount} via {Name}" zurückgibt.
  • CreditCardPayment.cs: Definieren Sie eine Klasse CreditCardPayment im Namespace Payments, die von PaymentMethod erbt. Fügen Sie eine Eigenschaft CardNumber (string) hinzu. Der Konstruktor akzeptiert Name und Kartennummer und verwendet base, um den Namen zu übergeben. Überschreiben Sie ProcessPayment mit dem Schlüsselwort sealed, 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 eine sealed Klasse namens BankTransfer im Namespace Payments, die von PaymentMethod erbt. Diese gesamte Klasse ist versiegelt – niemand kann von ihr erben. Fügen Sie eine Eigenschaft BankName (string) hinzu. Der Konstruktor akzeptiert Name und Bankname. Überschreiben Sie ProcessPayment, 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 als PaymentMethod-Referenzen und rufen Sie ProcessPayment fü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 National

Verwenden 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
    }
}
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