C# の sealed class とは?いつ使う?落とし穴は?
C# の sealed は一言でいうと 「継承を禁止する」 ためのキーワードです。
クラスに付けると、そのクラスは 派生クラス(サブクラス)を作れなくなります。
public sealed class PaymentResult
{
public bool IsSuccess { get; }
public string Message { get; }
public PaymentResult(bool isSuccess, string message)
{
IsSuccess = isSuccess;
Message = message;
}
}
この場合、次のような継承はコンパイルエラーになります。
public class SpecialPaymentResult : PaymentResult // ❌ sealed なので継承不可
{
}
sealed class を使う代表的な理由
1) 設計として「これ以上拡張させたくない」を明示する
継承は便利ですが、派生先で挙動が変わると安全性が落ちます。
- セキュリティ上、振る舞いを変えられると困る
- ライブラリの公開 API で、互換性を守りたい
- ドメインモデルで「この概念はこれ以上派生しない」を表現したい
2) 不変・値オブジェクト的なクラスを固定化したい
「継承でプロパティ増やされたら同値性が崩れる」みたいな事故を防ぎやすいです。
3) virtual のオーバーライドを禁止できる(性能というより安全性)
sealed class にすると、そもそも継承できないので override されません。
sealed は class だけ?実は “メソッド” も sealed にできる
sealed は メソッドにも付けられます(ただし条件あり)。
-
overrideしているメソッドに対して - それ以上のオーバーライドを禁止する用途
public class Base
{
public virtual void Execute() { }
}
public class Derived : Base
{
public sealed override void Execute()
{
// ここで最終確定
}
}
public class MoreDerived : Derived
{
// public override void Execute() {} // ❌ sealed override なので不可
}
sealed class の「メリット / デメリット」
メリット
- 拡張されないことが保証され、設計意図が明確
- 想定外のオーバーライドを防げる(特に public API)
- “継承で壊れる” 系のバグを未然に防ぎやすい
デメリット
- テストで モックしづらい(継承ベースのモックが使えない)
- 後から「やっぱり派生したい」が発生すると設計変更になる
- “拡張ポイント” を奪うので、ライブラリ設計では慎重さが必要
実務でのおすすめパターン(クリーンアーキテクチャ前提)
✅ 1) 外部公開する DTO / Result を sealed にする
派生で勝手に増やされると、シリアライズや互換性が崩れがちです。
public sealed class UserProfileDto
{
public long UserId { get; init; }
public string HandleName { get; init; } = "";
}
✅ 2) Domain の Value Object を sealed にする
同値性や不変性を守りやすい。
✅ 3) “継承させたい” ときは interface か composition を優先
「差し替えたい=継承」ではなく、依存性は interface に寄せた方が扱いやすいです。
public interface IClock
{
DateTime UtcNow { get; }
}
public sealed class SystemClock : IClock
{
public DateTime UtcNow => DateTime.UtcNow;
}
この形なら SystemClock は sealed でも、テストでは IClock を差し替えできます。
sealed にすべきか迷ったときの判断基準
sealed 推奨
- 派生されると仕様が壊れる(セキュリティ/整合性/互換性)
- 値オブジェクト・DTO・結果モデルなど「固定したい概念」
- 外部公開のライブラリ API(互換性を守りたい)
sealed にしない
- 拡張ポイントとして継承を提供したいフレームワーク設計
- プラグイン方式を想定している
- テストで継承モック前提(ただし interface で回避しやすい)
よくある勘違い
「sealed にすると速くなる?」
“結果として” 最適化が効くケースはありますが、性能目的で sealed を付けるのは優先度低めです。
基本は 設計の意図(継承禁止) で選ぶ方が安全です。
「static class と sealed class は同じ?」
違います。
-
static class:インスタンス化不可 + 継承不可(静的メンバーのみ) -
sealed class:インスタンス化可能だが継承不可
まとめ
-
sealed classは 継承禁止 - 設計意図を明確にし、想定外の拡張を防ぐのに強い
- テスト容易性は interface / composition で担保するのが現代的
- 性能目的より、壊れない設計のために使うのがおすすめ