Menu
Coddy logo textTech

Klasy sealed

Część sekcji Programowanie obiektowe ścieżki C# w Coddy. Lekcja 22 z 70.

Czasami chcesz przerwać łańcuch dziedziczenia. Słowo kluczowe sealed uniemożliwia dziedziczenie po klasie, blokując jej implementację, tak aby żadna inna klasa nie mogła jej rozszerzyć.

public sealed class FinalDog : Animal
{
    public override void Speak()
    {
        Console.WriteLine("Woof!");
    }
}

// To spowodowałoby błąd kompilacji:
// public class SuperDog : FinalDog { }

Dlaczego warto oznaczyć klasę jako zapieczętowaną? Bezpieczeństwo i integralność projektu to częste powody. Jeśli starannie zaprojektujesz klasę i nie chcesz, by ktoś zmieniał jej działanie przez dziedziczenie, zapieczętowanie jej gwarantuje, że Twoja implementacja pozostanie nienaruszona.

Możesz też zapieczętować pojedyncze metody zamiast całych klas. Uniemożliwia to ich dalsze nadpisywanie, a jednocześnie pozwala dziedziczyć po samej klasie:

public class Dog : Animal
{
    public sealed override void Speak()
    {
        Console.WriteLine("Woof!");
    }
}

public class Puppy : Dog
{
    // Nie można przesłonić Speak() — jest oznaczona jako sealed
    // Można jednak dodawać nowe metody
    public void Whimper()
    {
        Console.WriteLine("Whimper...");
    }
}

Zauważ, że sealed w przypadku metody musi być połączone z override. Możesz zapieczętować tylko metodę, która była już wirtualna w klasie nadrzędnej. Daje ci to precyzyjną kontrolę: pozwól na pewne dostosowania w hierarchii dziedziczenia, a następnie zablokuj je w określonym miejscu.

challenge icon

Wyzwanie

Łatwy

Zbudujmy system przetwarzania płatności, który pokazuje, jak używać sealed, aby zablokować możliwość modyfikowania kluczowych implementacji. Utworzysz hierarchię, w której niektóre metody płatności można rozszerzać, ale te najbezpieczniejsze są zapieczętowane, aby zapobiec wszelkim modyfikacjom.

Utworzysz cztery pliki, aby uporządkować swój kod:

  • PaymentMethod.cs: Zdefiniuj klasę PaymentMethod w przestrzeni nazw Payments. Ta klasa bazowa ma właściwość Name (string) oraz konstruktor, który ją ustawia. Dodaj metodę virtual o nazwie ProcessPayment(decimal amount), która zwraca "Processing ${amount} via {Name}".
  • CreditCardPayment.cs: Zdefiniuj klasę CreditCardPayment w przestrzeni nazw Payments, która dziedziczy po PaymentMethod. Dodaj właściwość CardNumber (string). Konstruktor przyjmuje nazwę i numer karty, używając base, aby przekazać nazwę. Przesłoń metodę ProcessPayment z użyciem słowa kluczowego sealed, aby żadna klasa pochodna nie mogła zmienić tego działania. Metoda powinna zwracać "Charging ${amount} to card ending in {last 4 digits of CardNumber}".
  • BankTransfer.cs: Zdefiniuj klasę sealed o nazwie BankTransfer w przestrzeni nazw Payments, która dziedziczy po PaymentMethod. Cała ta klasa jest zapieczętowana — nikt nie może po niej dziedziczyć. Dodaj właściwość BankName (string). Konstruktor przyjmuje nazwę i nazwę banku. Przesłoń metodę ProcessPayment, aby zwracała "Transferring ${amount} from {BankName}".
  • Program.cs: W pliku głównym utwórz instancje wszystkich trzech typów płatności, używając wartości wejściowych. Zapisz je jako referencje typu PaymentMethod i wywołaj dla każdej metodę ProcessPayment, aby pokazać polimorfizm z zapieczętowanymi implementacjami.

Otrzymasz trzy dane wejściowe:

  • Numer karty kredytowej (16 cyfr)
  • Nazwa banku
  • Kwota płatności

Wypisz wynik przetworzenia płatności każdą z metod:

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

Na przykład, jeśli danymi wejściowymi są 1234567890123456, First National i 150.00, wynik powinien być następujący:

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

Użyj "Generic" jako nazwy instancji bazowej klasy PaymentMethod, "Credit Card" dla płatności kartą kredytową oraz "Bank Transfer" dla przelewu bankowego.

Zapieczętowana metoda w CreditCardPayment gwarantuje, że jeśli ktoś utworzy klasę pochodną, nie będzie mógł zmienić sposobu przetwarzania płatności kartą kredytową. Zapieczętowana klasa BankTransfer całkowicie uniemożliwia dziedziczenie — oba te rozwiązania są ważnymi wzorcami bezpieczeństwa w systemach płatności!

Spróbuj swoich sił

using System;
using Payments;

class Program
{
    public static void Main(string[] args)
    {
        // Wczytaj dane wejściowe
        string cardNumber = Console.ReadLine();
        string bankName = Console.ReadLine();
        decimal amount = Convert.ToDecimal(Console.ReadLine());
        
        // TODO: Utwórz instancję bazowej klasy PaymentMethod o nazwie "Generic"
        // Przechowuj ją w referencji typu PaymentMethod
        
        // TODO: Utwórz instancję CreditCardPayment o nazwie "Credit Card" i podanym numerze karty
        // Przechowuj ją w referencji typu PaymentMethod
        
        // TODO: Utwórz instancję BankTransfer o nazwie "Bank Transfer" i podanej nazwie banku
        // Przechowuj ją w referencji typu PaymentMethod
        
        // TODO: Wywołaj ProcessPayment dla każdej z nich i wypisz wyniki
        // Każdy wynik powinien znaleźć się w osobnym wierszu
    }
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator C# online