object-oriented programming
class sealing
software development
encapsulation
C#

Why seal a class?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Sealing a class means declaring that no one can inherit from it. In C# and similar object-oriented languages, that is a deliberate design choice used to protect invariants, simplify reasoning, and communicate that the class is meant to be used as-is rather than as a base type.

What sealed Means In Practice

In C#, a sealed class cannot be subclassed:

csharp
1public sealed class CurrencyFormatter
2{
3    public string Format(decimal amount) => $"${amount:F2}";
4}

This will not compile:

csharp
public class CustomFormatter : CurrencyFormatter
{
}

That restriction is the whole point. The class author is saying that inheritance is not part of the intended extension model.

Protect Invariants And Behavior

One strong reason to seal a class is to preserve its guarantees.

Suppose a class enforces security-sensitive checks or carefully maintains internal consistency. If arbitrary subclasses can override methods or extend behavior unexpectedly, those guarantees become harder to trust.

Example:

csharp
1public sealed class ApiToken
2{
3    public string Value { get; }
4
5    public ApiToken(string value)
6    {
7        if (string.IsNullOrWhiteSpace(value))
8            throw new ArgumentException("Token cannot be empty.", nameof(value));
9
10        Value = value;
11    }
12}

Sealing this class makes the type simpler to reason about because callers know there is no derived type silently changing its semantics.

Avoid Inheritance As An Accidental Extension Point

If a class was not designed for inheritance, leaving it open can create fragile APIs. Consumers may derive from it, override members, and start depending on internal details that were never intended as extension points.

Later, if the library author changes the implementation, those subclasses may break.

Sealing communicates a cleaner contract:

  • here is the behavior you can rely on
  • composition is preferred over inheritance
  • future internal refactoring is less risky

That is often a better public API story than "inherit if you want and hope it works."

It Can Improve Performance, But That Is Secondary

Because the runtime knows a sealed class cannot be further overridden, it can sometimes optimize calls more aggressively. This is real, but it is usually not the primary reason to seal a class.

Design clarity is the stronger argument. Performance benefits are a bonus when they happen.

In other words, do not seal everything just because the JIT might like it. Seal classes when inheritance would be misleading or unsafe.

Prefer Composition When You Do Not Need A Base Type

Many classes are not natural bases. They are just concrete services or value-like helpers.

Instead of inheritance:

csharp
1public sealed class EmailSender
2{
3    public void Send(string to, string body)
4    {
5        Console.WriteLine($"Sending to {to}: {body}");
6    }
7}

If behavior needs to vary, use an interface:

csharp
1public interface IEmailSender
2{
3    void Send(string to, string body);
4}
5
6public sealed class SmtpEmailSender : IEmailSender
7{
8    public void Send(string to, string body)
9    {
10        Console.WriteLine($"SMTP to {to}: {body}");
11    }
12}

This is usually a better extension model than deriving from a concrete class that was never meant to be customized.

When Not To Seal

Do not seal a class if inheritance is part of the actual design.

Examples:

  • framework base classes
  • test doubles built through subclassing
  • domain abstractions intentionally meant to be specialized

If users are expected to override behavior, sealing gets in the way.

The decision should follow the class's role, not habit.

C# also lets you seal individual overrides:

csharp
1public class Base
2{
3    public virtual void Run() { }
4}
5
6public class Middle : Base
7{
8    public sealed override void Run() { }
9}

This is a narrower tool. It stops further overrides of one virtual member while still allowing the class itself to be inherited. It serves the same general goal: limiting extension where it would be unsafe or confusing.

Common Pitfalls

The biggest mistake is treating inheritance as the default extension mechanism for every class. Many concrete classes are better left sealed and composed instead.

Another mistake is sealing a class too aggressively in a library that genuinely expects user specialization. If derivation is part of the contract, sealing is the wrong signal.

People also sometimes justify sealing only with micro-optimizations. Performance may improve, but API clarity and behavioral safety are the stronger reasons.

Finally, leaving a class unsealed "just in case" can be costly. Once consumers start inheriting from it, the class becomes harder to change safely.

Summary

  • Sealing a class prevents inheritance and narrows the public extension surface.
  • It helps protect invariants and makes concrete behavior easier to trust.
  • Sealed classes often communicate "use composition or interfaces instead of subclassing."
  • Performance benefits can exist, but they are usually secondary to design clarity.
  • Do not seal classes that are genuinely intended to serve as base types.

Course illustration
Course illustration

All Rights Reserved.