closed (Referencia de C#)

A partir de C# 15, puede aplicar el closed modificador a una clase para declarar una jerarquía cerrada. Solo puede derivar un subtipo directo de una clase cerrada dentro de su ensamblado declarante. Dado que el conjunto de descendientes directos es fijo, una switch expresión que controla cada descendiente directo agota el tipo base cerrado y no necesita un brazo predeterminado.

// 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

La restricción del mismo ensamblado solo se aplica a descendientes directos de la clase cerrada. Una clase que deriva de una clase cerrada no se cierra a menos que también se closedmarque . Dado que Failed en el ejemplo anterior es un registro sin formato, otro ensamblado puede derivar de él:

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

Si también quiere evitar la derivación Failed , declárela como sealed o closed.

La documentación de referencia del lenguaje C# cubre la versión más reciente publicada del lenguaje C#. También contiene documentación inicial sobre las características de las versiones preliminares públicas de la próxima versión del lenguaje.

La documentación identifica cualquier característica introducida por primera vez en las últimas tres versiones del idioma o en las versiones preliminares públicas actuales.

Tip

Para buscar cuándo se introdujo por primera vez una característica en C#, consulte el artículo sobre el historial de versiones del lenguaje C#.

Nota:

closed es una palabra clave contextual. Tiene un significado especial solo cuando aparece como un modificador en una declaración de clase. Puede seguir usando closed como identificador en otros contextos. Si necesita usar closed como identificador en una posición en la que el modificador también sería válido, prefijo con @ (por ejemplo, @closed) para indicar al compilador que lo trate como un identificador en lugar del modificador.

Reglas de declaración

El closed modificador es un modificador de clase:

  • Una closed clase es implícitamente abstract. No se puede combinar closed con sealed, staticni con un modificador explícito abstract .
  • Debe declarar un subtipo directo de una clase cerrada en el mismo ensamblado y módulo que la clase base cerrada.
  • Una clase que deriva de una clase cerrada no está cerrada. Vuelva a aplicar el closed modificador si desea que también se cierre una clase derivada.

Si una clase genérica deriva directamente de una closed clase, todos los parámetros de tipo de la clase derivada deben usarse en la especificación de clase base. Esta regla no trata sobre el closed propio modificador: un tipo construido cerrado es un tipo genérico cuyos argumentos de tipo se especifican por completo (como Tree<int>), en lugar de un tipo abierto como Tree<T>. La regla garantiza que cada tipo construido cerrado de la clase base tenga exactamente un tipo construido cerrado correspondiente entre sus descendientes directos, por lo que el compilador puede razonar sobre la exhaustiva.

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

Expresiones de modificador exhaustivas

Cuando una switch expresión controla todos los descendientes directos de una clase cerrada, el compilador considera el modificador exhaustivo y no genera una advertencia de no exhaustiva:

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.
};

Cuando el modificador que rige la expresión es nullable, null se convierte en otro valor posible que el modificador debe controlar. Un modificador JobStatus? solo es exhaustivo cuando también cubre 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.
};

Si omite el null brazo, el compilador advierte de que el patrón null no se controla. La misma regla se aplica si el tipo cerrado es una clase o una estructura levantada a un tipo que acepta valores NULL.

Para obtener más información sobre cómo determina el compilador la exhaustiva, incluida la forma en que las jerarquías cerradas interactúan con restricciones genéricas y accesibilidad, consulte Patrones de jerarquía cerrados.

Parámetros de tipo restringidos a un tipo cerrado

Un parámetro de tipo restringido a una clase cerrada se trata como esa clase cerrada para las comprobaciones de exhaustivas. Una switch expresión cuyo valor de gobernanza tiene este parámetro de tipo es exhaustivo cuando controla cada descendiente directo de la restricción cerrada:

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.
};

Esta regla aplica si el parámetro de tipo aparece en un método o en el tipo contenedor.

Especificación del lenguaje C#

Para obtener más información, consulte la especificación de características Jerarquías cerradas .

Consulte también