Novità di C# 15

C# 15 include le nuove funzionalità seguenti. Provare queste funzionalità usando la versione più recente di Visual Studio 2026 Insider o .NET 11 Preview SDK:

C# 15 è la versione di anteprima C# più recente. Le versioni di anteprima di .NET 11 supportano C# 15. Per ulteriori informazioni, vedere versionamento del linguaggio C#.

È possibile scaricare la versione più recente di .NET 11 Preview SDK dalla pagina di download di .NET. È anche possibile scaricare Visual Studio 2026 Insider, che include .NET 11 Preview SDK.

La pagina "Novità in C#" aggiunge nuove funzionalità quando sono disponibili nelle versioni di anteprima pubblica. La sezione working set della pagina di stato della funzionalità roslyn tiene traccia quando le funzionalità future vengono unite nel ramo principale.

È possibile trovare eventuali modifiche di rilievo introdotte in C# 15 nell'articolo sulle modifiche di rilievo.

Annotazioni

Microsoft è interessato ai commenti e suggerimenti su queste funzionalità. Se trovi questioni con una di queste nuove funzionalità, crea un nuovo problema nel repository dotnet/roslyn.

Argomenti dell'espressione di raccolta

È possibile passare argomenti al costruttore o al metodo factory della raccolta sottostante usando un with(...) elemento come primo elemento in un'espressione di raccolta. Questa funzionalità consente di specificare capacità, comparer o altri parametri del costruttore direttamente all'interno della sintassi dell'espressione di raccolta.

Nell'esempio seguente viene illustrato come passare un argomento di capacità a un List<T> costruttore e un comparatore a un HashSet<T> oggetto:

string[] values = ["one", "two", "three"];

// Pass capacity argument to List<T> constructor
List<string> names = [with(capacity: values.Length * 2), .. values];

// Pass comparer argument to HashSet<T> constructor
HashSet<string> set = [with(StringComparer.OrdinalIgnoreCase), "Hello", "HELLO", "hello"];
// set contains only one element because all strings are equal with OrdinalIgnoreCase

Per altre informazioni sugli argomenti delle espressioni di raccolta, vedere l'articolo di riferimento sul linguaggio sulle espressioni di raccolta o sulla specifica delle funzionalità. Per informazioni sull'uso degli argomenti dell'espressione di raccolta negli inizializzatori di raccolta, vedere Inizializzatori di oggetti e raccolte.

Tipi unione

C# 15 introduce tipi di unione, che rappresentano un valore che può essere uno dei diversi tipi di case. Dichiarare un'unione con la union parola chiave :

public record class Cat(string Name);
public record class Dog(string Name);
public record class Bird(string Name);

public union Pet(Cat, Dog, Bird);

Le unioni forniscono conversioni implicite da ogni tipo di caso e il compilatore garantisce che switch le espressioni siano esaustive in tutti i tipi di caso.

Pet pet = new Dog("Rex");

string name = pet switch
{
    Dog d => d.Name,
    Cat c => c.Name,
    Bird b => b.Name,
};

Il runtime include i tipi UnionAttribute e IUnion che iniziano con .NET 11 Preview 5. Alcune funzionalità della specifica della proposta non sono ancora implementate. Queste funzionalità saranno disponibili nelle anteprime future.

Per altre informazioni, vedere Tipi di unione nella guida di riferimento al linguaggio o nella specifica delle funzionalità.

Gerarchie chiuse

A partire da C# 15, è possibile applicare il closed modificatore a una classe per dichiarare una gerarchia chiusa. Una classe chiusa può essere derivata solo dall'interno dell'assembly dichiarante, che corregge il set di discendenti diretti in fase di compilazione:

public closed record class GateState;
public record class Closed : GateState;
public record class Open(float Percent) : GateState;

Poiché il compilatore conosce ogni discendente diretto, un'espressione switch che gestisce ognuno di essi è esaustiva e non richiede un arm predefinito:

string Describe(GateState state) => state switch
{
    Closed => "closed",
    Open(var percent) => $"{percent}% open",
    // No warning: every direct descendant of 'GateState' is handled.
};

Il closed modificatore è una parola chiave contestuale. Una closed classe è implicitamente abstract e non può essere combinata con sealed, statico un modificatore esplicito abstract . La derivazione non è transitiva: un discendente non chiuso di una classe chiusa può comunque essere derivato da in altri assembly. Per estendere il controllo di esaustività ai livelli inferiori della gerarchia, contrassegna anche i discendenti intermedi closed.

Annotazioni

In C# 15 Preview 5, il runtime non include ancora System.Runtime.CompilerServices.ClosedAttribute. Fino a quando non lo fa, ogni progetto che usa il closed modificatore deve dichiarare l'attributo stesso:

namespace System.Runtime.CompilerServices;

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public sealed class ClosedAttribute : Attribute { }

Per altre informazioni, vedere i modelli di modifica chiusi e Gerarchia chiusa nel riferimento al linguaggio o la specifica della funzionalità. È possibile copiare gli esempi in questa sezione, inclusa la soluzione alternativa ClosedAttribute, dal progetto di frammenti di codice keywords nel repository dotnet/docs GitHub.

Sicurezza della memoria

C# 15 avvia un’iniziativa articolata su più versioni per ridefinire la sicurezza della memoria nel linguaggio. L'obiettivo è collegare il unsafe contesto alle operazioni che accedono effettivamente alla memoria non gestita, anziché all'esistenza di tipi di puntatore. La maggior parte delle vulnerabilità relative alla sicurezza della memoria deriva da queste operazioni di accesso, quindi il linguaggio le mette in evidenza per i revisori e i responsabili degli audit.

Nel modello completo, unsafe su un membro lo contrassegna come requires-unsafe: l'obbligo di audit ricade sul chiamante, che deve usare il membro all'interno di un contesto unsafe. Un assembly sceglie esplicitamente di adottare questa applicazione e il compilatore registra questa scelta con l'attributo System.Runtime.CompilerServices.MemorySafetyRulesAttribute. Il modello aggiunge anche una safe parola chiave contestuale che contrassegna extern i membri e i campi di layout esplicito come sicuri. Insieme, queste regole rendono espliciti i limiti della potenziale memoria non sicura attraverso un programma.

Il primo passaggio comprende gli allentamenti del puntatore. Quando si esegue la compilazione con la versione del preview linguaggio, le operazioni seguenti non richiedono più un unsafe contesto:

  • Dichiarazione di un tipo di puntatore e acquisizione dell'indirizzo di una variabile con l'operatore & .
  • L'istruzione fixed, che blocca una variabile.
  • Conversione di un'espressione stackalloc in un puntatore.
  • L'operatore sizeof applicato a qualsiasi tipo non gestito.

L'esempio seguente crea e aggiunge un puntatore senza contesto unsafe :

int number = 42;
int* pointer = &number;

int[] numbers = [10, 20, 30];
fixed (int* first = numbers)
{
    // Dereferencing the pointer still requires an unsafe context.
}

Le operazioni che accedono alla memoria puntata, come la dereferenziazione del puntatore (*p), l'accesso ai membri tramite puntatore (p->member), l'accesso agli elementi tramite puntatore (p[i]) e l'invocazione di un puntatore a funzione, richiedono comunque un contesto unsafe.

Il modello di membro requires-unsafe, l'adesione esplicita dell'assembly alle regole aggiornate di sicurezza della memoria e la safe parola chiave contestuale saranno disponibili in una versione di anteprima successiva.

Per altre informazioni, vedi Codice non sicuro, tipi di puntatore e puntatori di funzione nel riferimento del linguaggio o nella specifica della funzionalità.

Vedere anche