Skip to content

Abstract Class vs Sealed Class in C#

Abstract and sealed classes are opposites, which is exactly why interviewers like to ask about them together:

  • An abstract class must be inherited. You can't create an object of it; it exists to be a base class.
  • A sealed class cannot be inherited. You can create objects of it, but no class can derive from it.

In this article we build a small notification system (email and SMS), where each concept has a real job, then look at the actual compiler errors you get when you break the rules. All output and errors below come from compiling and running the code with .NET 8.

Abstract class: a base that must be completed

Every notification in our system follows the same steps: write a log line, deliver the message, confirm it was sent. Only the delivery differs between email and SMS. An abstract class captures the common steps once and forces each channel to supply its own delivery:

public abstract class Notification
{
    // Abstract members: no body here; every child class MUST provide one
    protected abstract string Channel { get; }
    protected abstract void Deliver(string message);

    // Normal (concrete) method: shared by all children, written once
    public void Send(string message)
    {
        Console.WriteLine($"Sending via {Channel}...");
        Deliver(message);
        Console.WriteLine($"{Channel} sent.");
    }
}

public class EmailNotification : Notification
{
    private readonly string _to;
    public EmailNotification(string to) => _to = to;

    protected override string Channel => "Email";
    protected override void Deliver(string message) => Console.WriteLine($"  To {_to}: {message}");
}

Key points:

  • The abstract keyword on the class means "this class is incomplete".
  • abstract members have no body; a child class supplies it with override.
  • An abstract class can still have normal methods (Send), fields, properties and constructors. That's the big difference from an interface: it can share real code.
  • The shared Send method calls Deliver without knowing whether it's email or SMS. This is the Template Method pattern.

Sealed class: a final implementation

SMS delivery has carrier-specific rules (message length, sender IDs) that we don't want anyone to change by inheriting and overriding. So we seal it:

public sealed class SmsNotification : Notification
{
    private readonly string _phone;
    public SmsNotification(string phone) => _phone = phone;

    protected override string Channel => "SMS";
    protected override void Deliver(string message) => Console.WriteLine($"  To {_phone}: {message}");
}

// A small utility that should never be extended
public sealed class OtpService
{
    public string Generate() => Random.Shared.Next(100000, 1000000).ToString();
}

A sealed class can inherit from another class (here SmsNotification inherits from the abstract Notification); it just can't be inherited from.

Using them together

List<Notification> notifications =
[
    new EmailNotification("asha@example.com"),
    new SmsNotification("9876543210")
];

foreach (var n in notifications)
    n.Send("Your order #1042 has been shipped.");

var otp = new OtpService();
Console.WriteLine($"OTP length: {otp.Generate().Length}");
Output
Sending via Email...
  To asha@example.com: Your order #1042 has been shipped.
Email sent.
Sending via SMS...
  To 9876543210: Your order #1042 has been shipped.
SMS sent.
OTP length: 6

The list's type is the abstract base, Notification, so the same loop works for every channel. Adding WhatsApp later means writing one new class; the loop doesn't change.

What the compiler does when you break the rules

1. Creating an object of an abstract class

var n = new Notification();
Compiler error
error CS0144: Cannot create an instance of the abstract type or interface 'Notification'

2. Forgetting to implement an abstract member

class Invoice : Notification { }      // no Channel, no Deliver
Compiler errors
error CS0534: 'Invoice' does not implement inherited abstract member 'Notification.Channel.get'
error CS0534: 'Invoice' does not implement inherited abstract member 'Notification.Deliver(string)'

The compiler lists every abstract member that is missing. The only way out is to implement them all, or to declare the child class abstract too.

3. Inheriting from a sealed class

class PremiumSms : SmsNotification { }
Compiler error
error CS0509: 'PremiumSms': cannot derive from sealed type 'SmsNotification'

4. Giving an abstract method a body

abstract class Report
{
    public abstract void Print() { }
}
Compiler error
error CS0500: 'Report.Print()' cannot declare a body because it is marked abstract

5. Making a class both abstract and sealed

abstract sealed class Helper { }
Compiler error
error CS0418: 'Helper': an abstract type cannot be sealed or static

It makes no sense: abstract means "must be inherited" and sealed means "can't be inherited", so nothing could ever use the class.

Sealed methods

sealed can also be used on an overridden method, to stop further overriding down the inheritance chain while still allowing the class itself to be inherited:

public class EmailNotification : Notification
{
    // Children of EmailNotification can't override Deliver again
    protected sealed override void Deliver(string message) { ... }
}

You can only seal a method that is an override. Sealing an ordinary method is an error, because it was never virtual in the first place:

class Base
{
    public sealed void Log() { }
}
Compiler error
error CS0238: 'Base.Log()' cannot be sealed because it is not an override

Real examples from .NET

  • Sealed: System.String is sealed (typeof(string).IsSealed returns True), so nobody can change how strings behave by inheriting. Many framework types are sealed for safety and performance.
  • Abstract: System.IO.Stream is abstract; FileStream, MemoryStream and NetworkStream complete it. DbConnection and ASP.NET Core's ControllerBase are abstract too.
  • Static classes such as System.Math are compiled as abstract and sealed at the IL level (typeof(Math) reports IsAbstract=True and IsSealed=True): no objects and no inheritance. C# doesn't let you write abstract sealed yourself (error CS0418); you write static class instead.

Comparison table

PointAbstract classSealed class
PurposeTo be a base class that others completeTo be a final class that no one extends
Can create objects with new?No (CS0144)Yes
Can be inherited?Yes, that's its purposeNo (CS0509)
Can inherit from another class?YesYes
Abstract membersAllowed (children must override)Not allowed
Normal methods, fields, constructorsAllowedAllowed
Keyword on methodsabstract / virtualsealed override to stop further overriding
Typical useShared logic plus required steps (template method, base repositories, base controllers)Security-sensitive or final implementations, utility classes, small immutable types
.NET exampleStream, DbConnectionString

When to use which

  • Use an abstract class when several classes share real code and must each supply some specific part. If they share no code, an interface is usually better.
  • Use a sealed class when a class isn't designed to be extended: inheriting could break its rules or security, or you simply haven't designed for it. Many teams seal classes by default and unseal only when there's a real need. The JIT compiler can also optimise calls on sealed types slightly better.

Interview questions

1. What is the difference between an abstract class and a sealed class?

An abstract class can't be instantiated and must be inherited; it can contain abstract members that children must implement. A sealed class can be instantiated but can't be inherited. They are opposites.

2. Can an abstract class be sealed?

No. The compiler reports CS0418, because a class that must be inherited and can't be inherited could never be used. (A static class is compiled as abstract + sealed in IL, but you write it as static.)

3. Can an abstract class have a constructor?

Yes. You can't call it with new directly, but it runs when a child class is created, to initialise the shared part of the object.

4. Can a sealed class inherit from another class?

Yes. sealed only stops other classes from inheriting from it. In our example, the sealed SmsNotification inherits from the abstract Notification.

5. What is a sealed method?

An overridden method marked sealed override, which can't be overridden again further down the hierarchy. Only override methods can be sealed (otherwise CS0238).

6. Abstract class or interface?

Choose an abstract class when related classes share implementation code and state. Choose an interface when unrelated classes need the same capability, or when a class needs several of them, since a class can inherit only one class but implement many interfaces.

Summary

  • Abstract: can't be instantiated, must be inherited, may contain abstract members and shared code.
  • Sealed: can be instantiated, can't be inherited.
  • A class can't be both (CS0418); static classes are the special abstract + sealed case.
  • sealed override stops a method from being overridden further.
  • Use abstract for shared base logic with required steps, and sealed for final implementations.