closed (C# 参考)

从 C# 15 开始,可以将修饰符应用于 closed 类来声明 封闭层次结构。 只能从其声明程序集内的封闭类派生直接子类型。 由于直接后代集是固定的,因此 switch 处理每个直接后代的表达式会耗尽封闭基类型,并且不需要默认臂。

// 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 中派生,请将其声明为 sealedclosed

C# 语言参考记录了 C# 语言的最新发布版本。 它还包含即将发布的语言版本公共预览版中功能的初始文档。

本文档标识了在语言的最后三个版本或当前公共预览版中首次引入的任何功能。

Tip

若要查找 C# 中首次引入功能时,请参阅 有关 C# 语言版本历史记录的文章。

注释

closed 是上下文关键字。 仅当它在类声明上显示为修饰符时,它才具有特殊意义。 可以继续 closed 在其他上下文中用作标识符。 如果需要将 closed 修饰符用作有效位置的标识符,请为修饰符添加 @ 前缀(例如, @closed)告知编译器将其视为标识符而不是修饰符。

声明规则

修饰 closed 符是类修饰符:

  • closedabstract隐式的。 不能与显式abstract修饰符合并sealedclosedstatic
  • 必须在与关闭基类相同的程序集和模块中声明关闭类的直接子类型。
  • 派生自已关闭类的类本身不会关闭。 closed如果希望派生类也关闭,请再次应用修饰符。

如果泛型类直接派生自类 closed ,则必须在基类规范中使用派生类上的每个类型参数。 此规则与修饰符本身不相关 closed封闭构造类型 是一种泛型类型,其类型参数是完全指定的(例如 Tree<int>),而不是 像打开的类型 一样 Tree<T>。 该规则可确保基类的每个封闭构造类型在其直接后代中只有一个对应的封闭构造类型,因此编译器可以推断出详尽性。

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 将成为开关必须处理的另一个可能值。 仅当切换还涵盖null以下情况时,切换JobStatus?才详尽:

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# 语言规范

有关详细信息,请参阅 “关闭层次结构 ”功能规范。

另见