closed (C# リファレンス)

C# 15 以降では、 closed 修飾子をクラスに適用して 、閉じた階層を宣言できます。 宣言アセンブリ内の閉じたクラスから直接サブタイプのみを派生させることができます。 直接子孫のセットは固定されているため、各直接子孫を処理する switch 式は、閉じた基本型を使い果たし、既定の arm は必要ありません。

// Assembly 1
public closed record class JobStatus;
public record class Queued : JobStatus;
public record class Running(int PercentComplete) : JobStatus;
public record class Completed(TimeSpan Elapsed) : JobStatus;
public record class Failed(string Error) : JobStatus;

// Assembly 2
public record class Paused : JobStatus; // Error: 'JobStatus' is a closed class

同じアセンブリの制限は、閉じたクラスの 直接 の子孫にのみ適用されます。 閉じたクラスから派生したクラスは、 closedマークしない限り、それ自体は閉じられません。 前の例の Failed はプレーン レコードであるため、別のアセンブリから派生させることができます。

// Assembly 2
public record class RetryableFailed(string Error, int Attempts) : Failed(Error); // OK: 'Failed' isn't sealed or closed

Failedからの派生も禁止する場合は、sealedまたはclosedとして宣言します。

C# 言語リファレンスには、C# 言語の最新リリース バージョンが記載されています。 また、今後の言語リリースのパブリック プレビューの機能に関する初期ドキュメントも含まれています。

このドキュメントでは、言語の最後の 3 つのバージョンまたは現在のパブリック プレビューで最初に導入された機能を特定します。

Tip

C# で機能が初めて導入された時期を確認するには、 C# 言語バージョン履歴に関する記事を参照してください。

Note

closed はコンテキスト キーワードです。 クラス宣言の修飾子として表示される場合にのみ、特別な意味を持ちます。 closedは、他のコンテキストで識別子として引き続き使用できます。 修飾子も有効な位置で識別子として closed を使用する必要がある場合は、修飾子ではなく識別子として扱うようにコンパイラに指示するために、 @ (たとえば、 @closed) でプレフィックスを付けます。

宣言規則

closed修飾子はクラス修飾子です。

  • closed クラスは暗黙的にabstractclosedsealedstatic、または明示的なabstract修飾子と組み合わせることはできません。
  • 閉じた基底クラスと同じアセンブリおよびモジュールで、閉じたクラスの直接サブタイプを宣言する必要があります。
  • 閉じたクラスから派生したクラス自体は閉じられません。 派生クラスも閉じる場合は、 closed 修飾子をもう一度適用します。

ジェネリック クラスが closed クラスから直接派生する場合は、派生クラスのすべての型パラメーターを基底クラスの仕様で使用する必要があります。 この規則は、closed修飾子自体に関することではありません。閉じた構築型は、Tree<T>のようなオープン型ではなく、型引数が完全に指定されたジェネリック (Tree<int> など) です。 この規則により、基底クラスの閉じた構築済み型ごとに、その直接の子孫の中に 1 つの対応する閉じた構築型が正確に含まれるので、コンパイラは完全性を理由にすることができます。

public closed record class Tree<T>;

public record class Leaf<T>(T Value) : Tree<T>;                       // OK: 'T' appears in the base class
public record class Branch<T>(Tree<T> Left, Tree<T> Right) : Tree<T>; // OK: 'T' appears in the base class
public record class Constant<U>(U Value) : Tree<int> { } // Error: 'U' isn't used in the base class

完全な切り替え式

switch式が閉じたクラスのすべての直接の子孫を処理する場合、コンパイラはスイッチを完全と見なし、非網羅性警告を生成しません。

public static string Describe(JobStatus status) => status switch
{
    Queued => "waiting to start",
    Running(var percent) => $"{percent}% complete",
    Completed(var elapsed) => $"finished in {elapsed.TotalSeconds:F1}s",
    Failed(var error) => $"failed: {error}",
    // No warning: every direct descendant of 'JobStatus' is handled.
};

スイッチ 制御式が null 許容の場合、 null はスイッチが処理する必要がある別の可能な値になります。 JobStatus?の切り替えは、nullもカバーする場合にのみ網羅的です。

public static string DescribeOrUnknown(JobStatus? status) => status switch
{
    null => "unknown",
    Queued => "waiting to start",
    Running(var percent) => $"{percent}% complete",
    Completed(var elapsed) => $"finished in {elapsed.TotalSeconds:F1}s",
    Failed(var error) => $"failed: {error}",
    // No warning: every direct descendant of 'JobStatus' is handled, and null is handled.
};

null arm を省略すると、パターンnullが処理されていないことがコンパイラによって警告されます。 閉じた型がクラスであるか、null 許容型にリフトされた構造体であるかに関係なく、同じ規則が適用されます。

閉じた階層がジェネリック制約やアクセシビリティと対話する方法など、コンパイラが完全性を判断する方法の詳細については、「 閉じた階層パターン」を参照してください。

閉じた型に制約された型パラメーター

閉じたクラスに制約された型パラメーターは、網羅性チェックのために、その閉じたクラスとして扱われます。 このような型パラメーターを持つ制御値を持つ switch 式は、閉じられた制約のすべての直接子孫を処理する場合に完全です。

public static string DescribeJob<X>(X status) where X : JobStatus => status switch
{
    Queued => "waiting to start",
    Running(var percent) => $"{percent}% complete",
    Completed(var elapsed) => $"finished in {elapsed.TotalSeconds:F1}s",
    Failed(var error) => $"failed: {error}",
    // No warning: 'X' is constrained to a closed type, so its direct descendants exhaust the switch.
};

この規則は、型パラメーターがメソッドに表示されるか、含まれる型に対して表示されるかに関係なく適用されます。

C# 言語仕様

詳細については、 クローズド階層 機能の仕様を参照してください。

こちらも参照ください