closed (Riferimenti per C#)

A partire da C# 15, è possibile applicare il closed modificatore a una classe per dichiarare una gerarchia chiusa. È possibile derivare un sottotipo diretto solo da una classe chiusa all'interno dell'assembly dichiarante. Poiché il set di discendenti diretti è fisso, un'espressione switch che gestisce ogni discendente diretto esaurisce il tipo di base chiuso e non richiede un braccio predefinito.

// 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 restrizione dello stesso assembly si applica solo ai discendenti diretti della classe chiusa. Una classe che deriva da una classe chiusa non viene chiusa a meno che non venga contrassegnata closedanche da . Poiché Failed nell'esempio precedente è un record normale, un altro assembly può derivare da esso:

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

Se si vuole impedire anche la derivazione Failed , dichiararla come sealed o closed.

Il riferimento al linguaggio C# documenta la versione rilasciata più di recente del linguaggio C#. Contiene anche la documentazione iniziale per le funzionalità nelle versioni di anteprima pubblica per la prossima versione del linguaggio di programmazione.

La documentazione identifica tutte le funzionalità introdotte nelle ultime tre versioni della lingua o nelle anteprime pubbliche correnti.

Tip

Per trovare quando una funzionalità è stata introdotta per la prima volta in C#, vedere l'articolo sulla cronologia delle versioni del linguaggio C#.

Note

closed è una parola chiave contestuale. Ha un significato speciale solo quando viene visualizzato come modificatore in una dichiarazione di classe. È possibile continuare a usare closed come identificatore in altri contesti. Se è necessario usare closed come identificatore in una posizione in cui il modificatore sarebbe valido, anteporre il prefisso con @ (ad esempio, @closed) per indicare al compilatore di considerarlo come identificatore anziché come modificatore.

Regole di dichiarazione

Il closed modificatore è un modificatore di classe:

  • Una closed classe è implicitamente abstract. Non è possibile combinare closed con sealed, statico un modificatore esplicito abstract .
  • È necessario dichiarare un sottotipo diretto di una classe chiusa nello stesso assembly e modulo della classe base chiusa.
  • Una classe che deriva da una classe chiusa non è chiusa. Applicare di nuovo il closed modificatore se si vuole chiudere anche una classe derivata.

Se una classe generica deriva direttamente da una closed classe , ogni parametro di tipo nella classe derivata deve essere usato nella specifica della classe base. Questa regola non riguarda il closed modificatore stesso: un tipo costruito chiuso è un tipo generico i cui argomenti di tipo sono completamente specificati ,ad esempio Tree<int>, anziché un tipo aperto come Tree<T>. La regola garantisce che ogni tipo costruito chiuso della classe di base abbia esattamente un tipo costruito chiuso corrispondente tra i discendenti diretti, in modo che il compilatore possa ragionarsi sull'esaustività.

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

Espressioni switch complete

Quando un'espressione switch gestisce ogni discendente diretto di una classe chiusa, il compilatore considera il commutatore esaustivo e non genera un avviso di non esaustività:

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

Quando l'opzione che regola l'espressione è nullable, null diventa un altro valore possibile che l'opzione deve gestire. Un passaggio al passaggio JobStatus? è esaustivo solo quando copre nullanche :

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

Se si omette il null arm, il compilatore avvisa che il modello null non viene gestito. La stessa regola si applica se il tipo chiuso è una classe o uno struct lifted a un tipo nullable.

Per altre informazioni su come il compilatore determina l'esaustività, inclusa l'interazione delle gerarchie chiuse con vincoli generici e accessibilità, vedere Modelli di gerarchia chiusa.

Parametri di tipo vincolati a un tipo chiuso

Un parametro di tipo vincolato a una classe chiusa viene considerato come tale classe chiusa per controlli di completezza. Un'espressione switch il cui valore di governance ha un parametro di questo tipo è esaustivo quando gestisce ogni discendente diretto del vincolo chiuso:

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

Questa regola si applica se il parametro di tipo viene visualizzato in un metodo o nel tipo contenitore.

Specificazione del linguaggio C#

Per altre informazioni, vedere la specifica della funzionalità Gerarchie chiuse .

Vedere anche