Menu
Coddy logo textTech

Sealed Classes

Part of the Object Oriented Programming section of Coddy's C# journey — lesson 22 of 70.

Sometimes you want to stop the inheritance chain. The sealed keyword prevents a class from being inherited, locking down its implementation so no other class can extend it.

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

// This would cause a compile error:
// public class SuperDog : FinalDog { }

Why would you seal a class? Security and design integrity are common reasons. When you've carefully designed a class and don't want anyone changing its behavior through inheritance, sealing it guarantees your implementation stays intact.

You can also seal individual methods rather than entire classes. This stops further overriding while still allowing the class itself to be inherited:

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

public class Puppy : Dog
{
    // Cannot override Speak() - it's sealed
    // But can add new methods
    public void Whimper()
    {
        Console.WriteLine("Whimper...");
    }
}

Notice that sealed on a method must be combined with override. You can only seal a method that was already virtual in a parent class. This gives you precise control: allow some customization in the inheritance hierarchy, then lock it down at a specific point.

challenge icon

Challenge

Easy

Let's build a payment processing system that demonstrates how to use sealed to lock down critical implementations. You'll create a hierarchy where certain payment methods can be extended, but the most secure ones are sealed to prevent any modifications.

You'll create four files to organize your code:

  • PaymentMethod.cs: Define a PaymentMethod class in the Payments namespace. This base class has a Name property (string) and a constructor that sets it. Include a virtual method called ProcessPayment(decimal amount) that returns "Processing ${amount} via {Name}".
  • CreditCardPayment.cs: Define a CreditCardPayment class in the Payments namespace that inherits from PaymentMethod. Add a CardNumber property (string). The constructor accepts name and card number, using base to pass the name. Override ProcessPayment with the sealed keyword so no further derived classes can change this behavior. It should return "Charging ${amount} to card ending in {last 4 digits of CardNumber}".
  • BankTransfer.cs: Define a sealed class called BankTransfer in the Payments namespace that inherits from PaymentMethod. This entire class is sealed - no one can inherit from it. Add a BankName property (string). The constructor accepts name and bank name. Override ProcessPayment to return "Transferring ${amount} from {BankName}".
  • Program.cs: In your main file, create instances of all three payment types using input values. Store them as PaymentMethod references and call ProcessPayment on each to demonstrate polymorphism with sealed implementations.

You will receive four inputs:

  • Credit card number (16 digits)
  • Bank name
  • Payment amount

Print the result of processing a payment through each method:

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

For example, if the inputs are 1234567890123456, First National, and 150.00, the output should be:

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

Use "Generic" as the name for the base PaymentMethod instance, "Credit Card" for the credit card payment, and "Bank Transfer" for the bank transfer.

The sealed method in CreditCardPayment ensures that if someone creates a subclass, they cannot change how credit card payments are processed. The sealed class BankTransfer prevents any inheritance at all - both are important security patterns in payment systems!

Cheat sheet

The sealed keyword prevents a class from being inherited or a method from being overridden further in the inheritance chain.

Sealing a class:

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

// This would cause a compile error:
// public class SuperDog : FinalDog { }

Sealed classes are used for security and design integrity, ensuring the implementation cannot be modified through inheritance.

Sealing a method:

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

public class Puppy : Dog
{
    // Cannot override Speak() - it's sealed
    // But can add new methods
    public void Whimper()
    {
        Console.WriteLine("Whimper...");
    }
}

The sealed keyword on a method must be combined with override. You can only seal methods that were already virtual in a parent class. This allows customization up to a specific point in the hierarchy, then locks it down.

Try it yourself

using System;
using Payments;

class Program
{
    public static void Main(string[] args)
    {
        // Read inputs
        string cardNumber = Console.ReadLine();
        string bankName = Console.ReadLine();
        decimal amount = Convert.ToDecimal(Console.ReadLine());
        
        // TODO: Create a base PaymentMethod instance with name "Generic"
        // Store it as a PaymentMethod reference
        
        // TODO: Create a CreditCardPayment instance with name "Credit Card" and the card number
        // Store it as a PaymentMethod reference
        
        // TODO: Create a BankTransfer instance with name "Bank Transfer" and the bank name
        // Store it as a PaymentMethod reference
        
        // TODO: Call ProcessPayment on each and print the results
        // Each result should be on its own line
    }
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming