C# の sealed とは?継承を制御して設計を明確にする
C# の sealed をクラスとメソッドで使い分ける方法、判断の目安、注意点を短く解説します。
sealed とは
sealed は、C# で「ここから先の継承や上書きは許可しない」と示すキーワードです。型の使われ方を明確にし、意図しない拡張による不具合を防ぐために使います。
用途は二つあります。クラス全体を継承不可にする方法と、オーバーライドしたメソッドだけを最終実装にする方法です。
クラスを継承不可にする
public sealed class ApiClient
{
public void Send(string message)
{
// API を呼び出す
}
}
// コンパイル エラー
// public class CustomApiClient : ApiClient { }クラスに sealed を付けると、そのクラスを基底クラスにできなくなります。「このクラスの振る舞いは完成している」「派生クラスで変更されると安全性を保てない」というときに向いています。
たとえば、値オブジェクト、認証まわりの型、外部サービスを包む小さなクライアントなどは、むやみに継承を許可しないほうが読みやすくなることがあります。差し替えが必要なら、継承ではなくインターフェースを用意する設計も選べます。
メソッドを最終実装にする
sealed は、派生クラスで override したメソッドにも付けられます。この場合はクラスの継承を許しつつ、そのメソッドだけをこれ以上上書きできなくします。
public class Document
{
public virtual string GetTitle() => "Untitled";
}
public class Report : Document
{
public sealed override string GetTitle() => "Monthly report";
}Report は継承できますが、GetTitle はさらにオーバーライドできません。継承階層の途中で、ある振る舞いを確定したい場合に便利です。
判断の目安
使うか迷ったら、「この型を誰かが継承したときも、基底クラスの前提を安全に守れるか」を考えます。答えがノーなら、sealed を検討する価値があります。
- 継承による拡張を設計として想定していない
- 不変性や検証ルールを必ず守らせたい
- テスト用の差し替えをインターフェースで表現できる
一方で、フレームワークが継承による拡張を前提にしている場合や、利用者にカスタマイズを委ねたい場合は、最初から閉じないほうが自然です。
パフォーマンス目的だけで使う?
sealed により、実行環境が仮想呼び出しを最適化しやすくなる場合はあります。ただし通常は、体感できるほどの差にはなりません。性能のために先回りして付けるより、「継承を許可するか」という設計上の意思を優先するのがおすすめです。
まとめ
sealed は、単に制限を加える機能ではなく、「この型やメソッドはここまで」と境界を伝える言葉です。拡張ポイントを意図して用意するときだけ継承を開き、それ以外は必要に応じて閉じる。小さな判断ですが、後から読みやすく壊れにくいC#の設計につながります。