Novità in C# 9.0What's new in C# 9.0

C# 9,0 aggiunge le funzionalità e i miglioramenti seguenti al linguaggio C#:C# 9.0 adds the following features and enhancements to the C# language:

  • RecordRecords
  • Setter di sola inizializzazioneInit only setters
  • Istruzioni di primo livelloTop-level statements
  • Miglioramenti dei criteri di ricercaPattern matching enhancements
  • Interi di dimensioni nativeNative sized integers
  • Puntatori funzioneFunction pointers
  • Non visualizzare il flag di creazione localsinitSuppress emitting localsinit flag
  • Espressioni new con tipo di destinazioneTarget-typed new expressions
  • funzioni anonime statichestatic anonymous functions
  • Espressioni condizionali tipizzate di destinazioneTarget-typed conditional expressions
  • Tipi restituiti covariantiCovariant return types
  • GetEnumeratorSupporto dell'estensione per i foreach cicliExtension GetEnumerator support for foreach loops
  • Parametri di rimozione lambdaLambda discard parameters
  • Attributi per le funzioni localiAttributes on local functions
  • Inizializzatori di moduloModule initializers
  • Nuove funzionalità per i metodi parzialiNew features for partial methods

C# 9,0 è supportato in .NET 5.C# 9.0 is supported on .NET 5. Per ulteriori informazioni, vedere controllo delle versioni del linguaggio C#.For more information, see C# language versioning.

È possibile scaricare la versione più recente di .NET SDK dalla pagina dei download di .NET.You can download the latest .NET SDK from the .NET downloads page.

Tipi di recordRecord types

C# 9,0 introduce *tipi di record _, che sono un tipo di riferimento che fornisce metodi sintetizzati per fornire la semantica del valore per verificarne l'uguaglianza.C# 9.0 introduces *record types _, which are a reference type that provides synthesized methods to provide value semantics for equality. I record non sono modificabili per impostazione predefinita.Records are immutable by default.

I tipi di record semplificano la creazione di tipi di riferimento non modificabili in .NET.Record types make it easy to create immutable reference types in .NET. Storicamente, i tipi .NET vengono classificati in gran parte come tipi di riferimento (incluse classi e tipi anonimi) e tipi di valore (inclusi struct e Tuple).Historically, .NET types are largely classified as reference types (including classes and anonymous types) and value types (including structs and tuples). Mentre i tipi di valore non modificabili sono consigliati, i tipi di valore modificabili non introducono spesso errori.While immutable value types are recommended, mutable value types don’t often introduce errors. Le variabili di tipo valore contengono i valori in modo che le modifiche vengano apportate a una copia dei dati originali quando i tipi di valore vengono passati ai metodi.Value type variables hold the values so changes are made to a copy of the original data when value types are passed to methods.

Sono disponibili molti vantaggi anche per i tipi di riferimento non modificabili.There are many advantages to immutable reference types as well. Questi vantaggi sono più evidenti nei programmi simultanei con dati condivisi.These advantages are more pronounced in concurrent programs with shared data. Sfortunatamente, C# ha forzato a scrivere un po' di codice aggiuntivo per creare tipi di riferimento non modificabili.Unfortunately, C# forced you to write quite a bit of extra code to create immutable reference types. I record forniscono una dichiarazione di tipo per un tipo di riferimento non modificabile che usa la semantica del valore per verificarne l'uguaglianza.Records provide a type declaration for an immutable reference type that uses value semantics for equality. I metodi sintetizzati per l'uguaglianza e i codici hash considerano due record uguali se le rispettive proprietà sono tutte uguali.The synthesized methods for equality and hash codes consider two records equal if their properties are all equal. Prendere in considerazione questa definizione:Consider this definition:

public record Person
{
    public string LastName { get; }
    public string FirstName { get; }

    public Person(string first, string last) => (FirstName, LastName) = (first, last);
}

La definizione del record crea un Person tipo che contiene due proprietà di sola lettura: FirstName e LastName .The record definition creates a Person type that contains two readonly properties: FirstName and LastName. Il Person tipo è un tipo di riferimento.The Person type is a reference type. Se si esamina il linguaggio il, è una classe.If you looked at the IL, it’s a class. Non è modificabile perché nessuna delle proprietà può essere modificata dopo che è stata creata.It’s immutable in that none of the properties can be modified once it's been created. Quando si definisce un tipo di record, il compilatore sintetizza diversi altri metodi:When you define a record type, the compiler synthesizes several other methods for you:

  • Metodi per i confronti di uguaglianza basati su valoriMethods for value-based equality comparisons
  • Esegui override per GetHashCode()Override for GetHashCode()
  • Copiare e clonare i membriCopy and Clone members
  • PrintMembers e ToString()PrintMembers and ToString()

I record supportano l'ereditarietà.Records support inheritance. È possibile dichiarare un nuovo record derivato da Person come indicato di seguito:You can declare a new record derived from Person as follows:

public record Teacher : Person
{
    public string Subject { get; }

    public Teacher(string first, string last, string sub)
        : base(first, last) => Subject = sub;
}

È anche possibile sigillare i record per impedire ulteriori derivazioni:You can also seal records to prevent further derivation:

public sealed record Student : Person
{
    public int Level { get; }

    public Student(string first, string last, int level) : base(first, last) => Level = level;
}

Il compilatore sintetizza versioni diverse dei metodi precedenti.The compiler synthesizes different versions of the methods above. Le firme del metodo dipendono da se il tipo di record è sealed e se la classe di base diretta è Object.The method signatures depend on if the record type is sealed and if the direct base class is object. I record devono avere le funzionalità seguenti:Records should have the following capabilities:

  • L'uguaglianza è basata sul valore e include un controllo per verificare che i tipi corrispondano.Equality is value-based, and includes a check that the types match. Ad esempio, un oggetto Student non può essere uguale a un Person , anche se i due record condividono lo stesso nome.For example, a Student can't be equal to a Person, even if the two records share the same name.
  • I record hanno una rappresentazione di stringa coerente generata automaticamente.Records have a consistent string representation generated for you.
  • I record supportano la costruzione di copia.Records support copy construction. La corretta costruzione della copia deve includere le gerarchie di ereditarietà e le proprietà aggiunte dagli sviluppatori.Correct copy construction must include inheritance hierarchies, and properties added by developers.
  • I record possono essere copiati con la modifica.Records can be copied with modification. Queste operazioni di copia e modifica supportano la mutazione non distruttiva.These copy and modify operations supports non-destructive mutation.

Oltre ai noti Equals Overload, operator == e operator != , il compilatore sintetizza una nuova EqualityContract Proprietà.In addition to the familiar Equals overloads, operator ==, and operator !=, the compiler synthesizes a new EqualityContract property. La proprietà restituisce un Type oggetto che corrisponde al tipo del record.The property returns a Type object that matches the type of the record. Se il tipo di base è object , la proprietà è virtual .If the base type is object, the property is virtual. Se il tipo di base è un altro tipo di record, la proprietà è un oggetto override .If the base type is another record type, the property is an override. Se il tipo di record è sealed , la proprietà è sealed .If the record type is sealed, the property is sealed. Il sintetizzato GetHashCode utilizza GetHashCode da tutte le proprietà e i campi dichiarati nel tipo di base e nel tipo di record.The synthesized GetHashCode uses the GetHashCode from all properties and fields declared in the base type and the record type. Questi metodi sintetizzati applicano l'uguaglianza basata sul valore all'interno di una gerarchia di ereditarietà.These synthesized methods enforce value-based equality throughout an inheritance hierarchy. Ciò significa che Student non verrà mai considerato uguale a un Person con lo stesso nome.That means a Student will never be considered equal to a Person with the same name. I tipi dei due record devono corrispondere e tutte le proprietà condivise tra i tipi di record sono uguali.The types of the two records must match as well as all properties shared among the record types being equal.

I record hanno anche un costruttore sintetizzato e un metodo di "clonazione" per la creazione di copie.Records also have a synthesized constructor and a "clone" method for creating copies. Il costruttore sintetizzato ha un solo parametro del tipo di record.The synthesized constructor has a single parameter of the record type. Produce un nuovo record con gli stessi valori per tutte le proprietà del record.It produces a new record with the same values for all properties of the record. Questo costruttore è privato se il record è sealed; in caso contrario, è protetto.This constructor is private if the record is sealed, otherwise it's protected. Il metodo "clone" sintetizzato supporta la costruzione di copie per le gerarchie di record.The synthesized "clone" method supports copy construction for record hierarchies. Il termine "clone" è racchiuso tra virgolette perché il nome effettivo è generato dal compilatore.The term "clone" is in quotes because the actual name is compiler generated. Non è possibile creare un metodo denominato Clone in un tipo di record.You can't create a method named Clone in a record type. Il metodo "clone" sintetizzato restituisce il tipo di record copiato mediante la distribuzione virtuale.The synthesized "clone" method returns the type of record being copied using virtual dispatch. Il compilatore aggiunge diversi modificatori per il metodo "clone" a seconda dei modificatori di accesso in record :The compiler adds different modifiers for the "clone" method depending on the access modifiers on the record:

  • Se il tipo di record è abstract , anche il metodo "clone" è abstract .If the record type is abstract, the "clone" method is also abstract. Se il tipo di base non è object , anche il metodo è override .If the base type isn't object, the method is also override.
  • Per i tipi di record che non sono abstract quando il tipo di base è object :For record types that aren't abstract when the base type is object:
    • Se il record è sealed , non viene aggiunto alcun modificatore aggiuntivo al metodo "clone" (ovvero non è virtual ).If the record is sealed, no additional modifiers are added to the "clone" method (meaning it is not virtual).
    • Se il record non è sealed , il metodo "clone" è virtual .If the record isn't sealed, the "clone" method is virtual.
  • Per i tipi di record che non sono abstract quando il tipo di base non è object :For record types that aren't abstract when the base type is not object:
    • Se il record è sealed , sarà anche il metodo "clone" sealed .If the record is sealed, the "clone" method is also sealed.
    • Se il record non è sealed , il metodo "clone" è override .If the record isn't sealed, the "clone" method is override.

Il risultato di tutte queste regole è che l'uguaglianza è implementata in modo coerente in qualsiasi gerarchia di tipi di record.The result of all these rules is the equality is implemented consistently across any hierarchy of record types. Due record sono uguali tra loro se le proprietà sono uguali e i loro tipi sono uguali, come illustrato nell'esempio seguente:Two records are equal to each other if their properties are equal and their types are the same, as shown in the following example:

var person = new Person("Bill", "Wagner");
var student = new Student("Bill", "Wagner", 11);

Console.WriteLine(student == person); // false

Il compilatore sintetizza due metodi che supportano l'output stampato: un ToString() override, e PrintMembers .The compiler synthesizes two methods that support printed output: a ToString() override, and PrintMembers. PrintMembersAccetta System.Text.StringBuilder come argomento.The PrintMembers takes a System.Text.StringBuilder as its argument. Viene aggiunto un elenco delimitato da virgole di nomi e valori di proprietà per tutte le proprietà nel tipo di record.It appends a comma-separated list of property names and values for all properties in the record type. PrintMembers chiama l'implementazione di base per tutti i record derivati da altri record.PrintMembers calls the base implementation for any records derived from other records. L' ToString() override restituisce la stringa prodotta da PrintMembers , racchiusa tra { e } .The ToString() override returns the string produced by PrintMembers, surrounded by { and }. Ad esempio, il ToString() metodo per Student restituisce un oggetto string come il codice seguente:For example, the ToString() method for Student returns a string like the following code:

"Student { LastName = Wagner, FirstName = Bill, Level = 11 }"

Gli esempi illustrati finora usano la sintassi tradizionale per dichiarare le proprietà.The examples shown so far use traditional syntax to declare properties. Esiste una forma più concisa denominata record posizionali.There's a more concise form called positional records. Di seguito sono riportati i tre tipi di record definiti in precedenza come record posizionali:Here are the three record types defined earlier as positional records:

public record Person(string FirstName, string LastName);

public record Teacher(string FirstName, string LastName,
    string Subject)
    : Person(FirstName, LastName);

public sealed record Student(string FirstName,
    string LastName, int Level)
    : Person(FirstName, LastName);

Queste dichiarazioni creano le stesse funzionalità della versione precedente (con due funzionalità aggiuntive descritte nella sezione seguente).These declarations create the same functionality as the earlier version (with a couple extra features covered in the following section). Queste dichiarazioni terminano con un punto e virgola anziché tra parentesi quadre, perché questi record non aggiungono altri metodi.These declarations end with a semicolon instead of brackets because these records don't add additional methods. È possibile aggiungere un corpo e includere anche eventuali metodi aggiuntivi:You can add a body, and include any additional methods as well:

public record Pet(string Name)
{
    public void ShredTheFurniture() =>
        Console.WriteLine("Shredding furniture");
}

public record Dog(string Name) : Pet(Name)
{
    public void WagTail() =>
        Console.WriteLine("It's tail wagging time");

    public override string ToString()
    {
        StringBuilder s = new();
        base.PrintMembers(s);
        return $"{s.ToString()} is a dog";
    }
}

Il compilatore produce un Deconstruct metodo per i record posizionali.The compiler produces a Deconstruct method for positional records. Il Deconstruct metodo ha parametri che corrispondono ai nomi di tutte le proprietà pubbliche nel tipo di record.The Deconstruct method has parameters that match the names of all public properties in the record type. Il Deconstruct metodo può essere usato per decostruire il record nelle proprietà del componente:The Deconstruct method can be used to deconstruct the record into its component properties:

var person = new Person("Bill", "Wagner");

var (first, last) = person;
Console.WriteLine(first);
Console.WriteLine(last);

Infine, registra le with espressionidi supporto.Finally, records support with expressions. Un * with expression_ _ indica al compilatore di creare una copia di un record, ma _WITH proprietà specificate modificate:A *with expression_ _ instructs the compiler to create a copy of a record, but _with specified properties modified:

Person brother = person with { FirstName = "Paul" };

La riga precedente crea un nuovo Person record in cui la LastName proprietà è una copia di person e l'oggetto FirstName è "Paul" .The previous line creates a new Person record where the LastName property is a copy of person, and the FirstName is "Paul". È possibile impostare un numero qualsiasi di proprietà in un' with espressione.You can set any number of properties in a with expression. È anche possibile usare with espressioni per creare una copia esatta.You can also use with expressions to create an exact copy. Specificare il set vuoto per le proprietà da modificare:You specify the empty set for the properties to modify:

Person clone = person with { };

Qualsiasi membro sintetizzato, ad eccezione del metodo "clone", può essere scritto dall'utente.Any of the synthesized members except the "clone" method may be written by you. Se un tipo di record dispone di un metodo che corrisponde alla firma di un metodo sintetizzato, il compilatore non sintetizza tale metodo.If a record type has a method that matches the signature of any synthesized method, the compiler doesn't synthesize that method. Nell'esempio di record precedente è Dog contenuto un metodo codificato a mano ToString() come esempio.The earlier Dog record example contains a hand coded ToString() method as an example.

Altre informazioni sui tipi di record in questa esercitazione sull' esplorazione dei record .Learn more about record types in this exploration of records tutorial.

Setter di sola inizializzazioneInit only setters

*I Setter solo init _ forniscono una sintassi coerente per inizializzare i membri di un oggetto.*Init only setters _ provide consistent syntax to initialize members of an object. Gli inizializzatori di proprietà rendono chiaro quale valore imposta la proprietà.Property initializers make it clear which value is setting which property. Lo svantaggio è che tali proprietà devono essere impostate.The downside is that those properties must be settable. A partire da C# 9,0, è possibile creare init funzioni di accesso anziché set funzioni di accesso per le proprietà e gli indicizzatori.Starting with C# 9.0, you can create init accessors instead of set accessors for properties and indexers. I chiamanti possono usare la sintassi dell'inizializzatore di proprietà per impostare questi valori nelle espressioni di creazione, ma tali proprietà sono ReadOnly una volta completata la costruzione.Callers can use property initializer syntax to set these values in creation expressions, but those properties are readonly once construction has completed. I setter solo init forniscono una finestra per modificare lo stato.Init only setters provide a window to change state. La finestra viene chiusa al termine della fase di costruzione.That window closes when the construction phase ends. La fase di costruzione termina in modo efficace dopo che tutte le inizializzazioni, inclusi gli inizializzatori di proprietà e le espressioni with sono state completate.The construction phase effectively ends after all initialization, including property initializers and with-expressions have completed.

È possibile dichiarare init solo Setter in qualsiasi tipo scritto.You can declare init only setters in any type you write. Ad esempio, lo struct seguente definisce una struttura di osservazione Meteo:For example, the following struct defines a weather observation structure:

public struct WeatherObservation
{
    public DateTime RecordedAt { get; init; }
    public decimal TemperatureInCelsius { get; init; }
    public decimal PressureInMillibars { get; init; }

    public override string ToString() =>
        $"At {RecordedAt:h:mm tt} on {RecordedAt:M/d/yyyy}: " +
        $"Temp = {TemperatureInCelsius}, with {PressureInMillibars} pressure";
}

I chiamanti possono usare la sintassi dell'inizializzatore di proprietà per impostare i valori, mantenendo al tempo stesso l'immutabilità:Callers can use property initializer syntax to set the values, while still preserving the immutability:

var now = new WeatherObservation 
{ 
    RecordedAt = DateTime.Now, 
    TemperatureInCelsius = 20, 
    PressureInMillibars = 998.0m 
};

Tuttavia, la modifica di un'osservazione dopo l'inizializzazione è un errore assegnando a una proprietà solo init al di fuori dell'inizializzazione:But, changing an observation after initialization is an error by assigning to an init-only property outside of initialization:

// Error! CS8852.
now.TemperatureInCelsius = 18;

I setter solo init possono essere utili per impostare le proprietà della classe base dalle classi derivate.Init only setters can be useful to set base class properties from derived classes. Possono inoltre impostare proprietà derivate tramite helper in una classe base.They can also set derived properties through helpers in a base class. I record posizionali dichiarano le proprietà usando solo Setter di inizializzazione.Positional records declare properties using init only setters. Questi Setter vengono usati nelle espressioni with.Those setters are used in with-expressions. È possibile dichiarare i setter solo init per qualsiasi class o struct definito.You can declare init only setters for any class or struct you define.

Istruzioni di primo livelloTop-level statements

Le istruzioni di primo livello rimuovono la cerimonia superflua da molte applicazioni.Top-level statements remove unnecessary ceremony from many applications. Si consideri il "Hello World!" canonicoConsider the canonical "Hello World!" programmaprogram:

using System;

namespace HelloWorld
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}

Esiste solo una riga di codice che esegue operazioni.There’s only one line of code that does anything. Con le istruzioni di primo livello, è possibile sostituire tutto questo standard con l' using istruzione e la singola riga che esegue il lavoro:With top-level statements, you can replace all that boilerplate with the using statement and the single line that does the work:

using System;

Console.WriteLine("Hello World!");

Se si desidera un programma a una riga, è possibile rimuovere la using direttiva e utilizzare il nome completo del tipo:If you wanted a one-line program, you could remove the using directive and use the fully qualified type name:

System.Console.WriteLine("Hello World!");

Solo un file nell'applicazione può utilizzare le istruzioni di primo livello.Only one file in your application may use top-level statements. Se il compilatore trova le istruzioni di primo livello in più file di origine, si tratta di un errore.If the compiler finds top-level statements in multiple source files, it’s an error. Si tratta di un errore anche se si combinano le istruzioni di primo livello con un metodo del punto di ingresso del programma dichiarato, in genere un Main metodo.It’s also an error if you combine top-level statements with a declared program entry point method, typically a Main method. In un certo senso, si può pensare che un file contenga le istruzioni che normalmente si trovano nel Main metodo di una Program classe.In a sense, you can think that one file contains the statements that would normally be in the Main method of a Program class.

Uno degli usi più comuni di questa funzionalità consiste nel creare materiale didattico.One of the most common uses for this feature is creating teaching materials. Gli sviluppatori C# principianti possono scrivere il "Hello World!" canonicoBeginner C# developers can write the canonical “Hello World!” in una o due righe di codice.in one or two lines of code. Non è necessaria alcuna cerimonia aggiuntiva.None of the extra ceremony is needed. Tuttavia, gli sviluppatori esperti troveranno molti usi anche per questa funzionalità.However, seasoned developers will find many uses for this feature as well. Le istruzioni di primo livello consentono un'esperienza simile a uno script per la sperimentazione simile a quella fornita da Jupyter notebook.Top-level statements enable a script-like experience for experimentation similar to what Jupyter notebooks provide. Le istruzioni di primo livello sono ottime per utilità e programmi console di piccole dimensioni.Top-level statements are great for small console programs and utilities. Funzioni di Azure è un caso d'uso ideale per le istruzioni di primo livello.Azure Functions are an ideal use case for top-level statements.

Soprattutto, le istruzioni di primo livello non limitano l'ambito o la complessità dell'applicazione.Most importantly, top-level statements don't limit your application’s scope or complexity. Tali istruzioni possono accedere a qualsiasi classe .NET o utilizzarla.Those statements can access or use any .NET class. Non limitano inoltre l'utilizzo di argomenti della riga di comando o valori restituiti.They also don’t limit your use of command-line arguments or return values. Le istruzioni di primo livello possono accedere a una matrice di stringhe denominate args.Top-level statements can access an array of strings named args. Se le istruzioni di primo livello restituiscono un valore integer, tale valore diventerà il codice integer restituito da un metodo sintetizzato Main .If the top-level statements return an integer value, that value becomes the integer return code from a synthesized Main method. Le istruzioni di primo livello possono contenere espressioni asincrone.The top-level statements may contain async expressions. In tal caso, il punto di ingresso sintetizzato restituisce Task o Task<int> .In that case, the synthesized entry point returns a Task, or Task<int>.

Miglioramenti dei criteri di ricercaPattern matching enhancements

C# 9 include nuovi miglioramenti ai criteri di ricerca:C# 9 includes new pattern matching improvements:

  • I modelli di tipo corrispondono a una variabile è un tipoType patterns match a variable is a type
  • I modelli tra parentesi applicano o enfatizzano la precedenza delle combinazioni di modelliParenthesized patterns enforce or emphasize the precedence of pattern combinations
  • I and modelli congiuntiva richiedono la corrispondenza di entrambi i modelliConjunctive and patterns require both patterns to match
  • I or modelli disgiuntiva richiedono la corrispondenza di uno schemaDisjunctive or patterns require either pattern to match
  • Per i *not modelli negati_*_ è necessario che un modello non corrispondaNegated not patterns require that a pattern doesn’t match
  • Per i modelli relazionali è necessario che l'input sia minore di, maggiore di, minore o uguale o maggiore o uguale a una costante specificata.Relational patterns require the input be less than, greater than, less than or equal, or greater than or equal to a given constant.

Questi modelli arricchiscono la sintassi per i modelli.These patterns enrich the syntax for patterns. Considerare i seguenti esempi:Consider these examples:

public static bool IsLetter(this char c) =>
    c is >= 'a' and <= 'z' or >= 'A' and <= 'Z';

In alternativa, con le parentesi facoltative per renderlo chiaro con and precedenza maggiore di or :Alternatively, with optional parentheses to make it clear that and has higher precedence than or:

public static bool IsLetterOrSeparator(this char c) =>
    c is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z') or '.' or ',';

Uno degli usi più comuni è una nuova sintassi per un controllo null:One of the most common uses is a new syntax for a null check:

if (e is not null)
{
    // ...
}

Ognuno di questi modelli può essere usato in qualsiasi contesto in cui sono consentiti i modelli: is espressioni di pattern, switch espressioni, modelli annidati e il modello dell'etichetta di un' switch istruzione case .Any of these patterns can be used in any context where patterns are allowed: is pattern expressions, switch expressions, nested patterns, and the pattern of a switch statement’s case label.

Prestazioni e interoperabilitàPerformance and interop

Tre nuove funzionalità migliorano il supporto per l'interoperabilità nativa e le librerie di basso livello che richiedono prestazioni elevate: integer a dimensione nativa, puntatori a funzione e omissione del localsinit flag.Three new features improve support for native interop and low-level libraries that require high performance: native sized integers, function pointers, and omitting the localsinit flag.

Gli Integer con dimensione nativa, nint e nuint , sono tipi Integer.Native sized integers, nint and nuint, are integer types. Sono espresse dai tipi sottostanti System.IntPtr e da System.UIntPtr .They're expressed by the underlying types System.IntPtr and System.UIntPtr. Il compilatore presenta le conversioni e le operazioni aggiuntive per questi tipi come int nativi.The compiler surfaces additional conversions and operations for these types as native ints. Gli Integer con dimensione nativa definiscono le proprietà per MaxValue o MinValue .Native sized integers define properties for MaxValue or MinValue. Questi valori non possono essere espressi come costanti in fase di compilazione perché dipendono dalle dimensioni native di un numero intero nel computer di destinazione.These values can't be expressed as compile time constants because they depend on the native size of an integer on the target machine. Tali valori sono di sola lettura in fase di esecuzione.Those values are readonly at runtime. È possibile utilizzare valori costanti per nint nell'intervallo [ int.MinValue ..You can use constant values for nint in the range [int.MinValue .. int.MaxValue].int.MaxValue]. È possibile utilizzare valori costanti per nuint nell'intervallo [ uint.MinValue ..You can use constant values for nuint in the range [uint.MinValue .. uint.MaxValue].uint.MaxValue]. Il compilatore esegue la riduzione costante per tutti gli operatori unari e binari usando i System.Int32 System.UInt32 tipi e.The compiler performs constant folding for all unary and binary operators using the System.Int32 and System.UInt32 types. Se il risultato non rientra in 32 bit, l'operazione viene eseguita in fase di esecuzione e non è considerata una costante.If the result doesn't fit in 32 bits, the operation is executed at runtime and isn't considered a constant. Gli Integer con dimensione nativa possono migliorare le prestazioni in scenari in cui la matematica di interi viene usata in modo estensivo e deve avere le prestazioni più rapide possibile.Native sized integers can increase performance in scenarios where integer math is used extensively and needs to have the fastest performance possible.

I puntatori a funzione forniscono una sintassi semplice per accedere ai codici operativi il ldftn e calli .Function pointers provide an easy syntax to access the IL opcodes ldftn and calli. È possibile dichiarare i puntatori a funzione usando la nuova delegate_ sintassi.You can declare function pointers using new delegate_ syntax. Un delegate* tipo è un tipo di puntatore.A delegate* type is a pointer type. La chiamata del delegate* tipo utilizza calli , a differenza di un delegato che utilizza callvirt sul Invoke() metodo.Invoking the delegate* type uses calli, in contrast to a delegate that uses callvirt on the Invoke() method. Sintatticamente, le chiamate sono identiche.Syntactically, the invocations are identical. La chiamata del puntatore a funzione usa la managed convenzione di chiamata.Function pointer invocation uses the managed calling convention. Aggiungere la unmanaged parola chiave dopo la delegate* sintassi per dichiarare che si desidera la unmanaged convenzione di chiamata.You add the unmanaged keyword after the delegate* syntax to declare that you want the unmanaged calling convention. È possibile specificare altre convenzioni di chiamata usando gli attributi nella delegate* dichiarazione.Other calling conventions can be specified using attributes on the delegate* declaration.

Infine, è possibile aggiungere System.Runtime.CompilerServices.SkipLocalsInitAttribute per indicare al compilatore di non emettere il localsinit flag.Finally, you can add the System.Runtime.CompilerServices.SkipLocalsInitAttribute to instruct the compiler not to emit the localsinit flag. Questo flag indica a CLR di inizializzare in modo zero tutte le variabili locali.This flag instructs the CLR to zero-initialize all local variables. Il localsinit flag è stato il comportamento predefinito per C# a partire da 1,0.The localsinit flag has been the default behavior for C# since 1.0. Tuttavia, l'inizializzazione zero aggiuntiva potrebbe avere un notevole effetto sulle prestazioni in alcuni scenari.However, the extra zero-initialization may have measurable performance impact in some scenarios. In particolare, quando si usa stackalloc .In particular, when you use stackalloc. In questi casi, è possibile aggiungere SkipLocalsInitAttribute .In those cases, you can add the SkipLocalsInitAttribute. È possibile aggiungerlo a un singolo metodo o a una proprietà oppure a un, class struct , interface o anche a un modulo.You may add it to a single method or property, or to a class, struct, interface, or even a module. Questo attributo non influisce sui abstract metodi e influisce sul codice generato per l'implementazione.This attribute doesn't affect abstract methods; it affects the code generated for the implementation.

Queste funzionalità possono migliorare le prestazioni in alcuni scenari.These features can improve performance in some scenarios. Devono essere usati solo dopo un'attenta benchmarking sia prima che dopo l'adozione.They should be used only after careful benchmarking both before and after adoption. Il codice che interessa gli Integer con dimensione nativa deve essere testato su più piattaforme di destinazione con dimensioni intere diverse.Code involving native sized integers must be tested on multiple target platforms with different integer sizes. Le altre funzionalità richiedono codice unsafe.The other features require unsafe code.

Adatta e termina le funzionalitàFit and finish features

Molte delle altre funzionalità consentono di scrivere codice in modo più efficiente.Many of the other features help you write code more efficiently. In C# 9,0 è possibile omettere il tipo in un' new espressione quando il tipo dell'oggetto creato è già noto.In C# 9.0, you can omit the type in a new expression when the created object's type is already known. L'uso più comune è nelle dichiarazioni di campo:The most common use is in field declarations:

private List<WeatherObservation> _observations = new();

newÈ possibile usare la tipizzazione di destinazione anche quando è necessario creare un nuovo oggetto da passare come argomento a un metodo.Target-typed new can also be used when you need to create a new object to pass as an argument to a method. Si consideri un ForecastFor() metodo con la firma seguente:Consider a ForecastFor() method with the following signature:

public WeatherForecast ForecastFor(DateTime forecastDate, WeatherForecastOptions options)

È possibile chiamarla come segue:You could call it as follows:

var forecast = station.ForecastFor(DateTime.Now.AddDays(2), new());

Un altro ottimo utilizzo di questa funzionalità consiste nel combinarlo con proprietà solo init per inizializzare un nuovo oggetto:Another nice use for this feature is to combine it with init only properties to initialize a new object:

WeatherStation station = new() { Location = "Seattle, WA" };

È possibile restituire un'istanza di creata dal costruttore predefinito usando un' return new(); istruzione.You can return an instance created by the default constructor using a return new(); statement.

Una funzionalità simile migliora la risoluzione del tipo di destinazione delle espressioni condizionali.A similar feature improves the target type resolution of conditional expressions. Con questa modifica, le due espressioni non devono disporre di una conversione implicita da una all'altra, ma possono avere entrambe le conversioni implicite in un tipo di destinazione.With this change, the two expressions need not have an implicit conversion from one to the other, but may both have implicit conversions to a target type. Probabilmente non si noterà questa modifica.You likely won’t notice this change. Ciò che si noterà è che alcune espressioni condizionali che in precedenza richiedevano i cast o non sarebbero state compilate ora funzionano semplicemente.What you will notice is that some conditional expressions that previously required casts or wouldn’t compile now just work.

A partire da C# 9,0, è possibile aggiungere il static modificatore alle espressioni lambda o ai metodi anonimi.Starting in C# 9.0, you can add the static modifier to lambda expressions or anonymous methods. Le espressioni lambda statiche sono analoghe alle static funzioni locali: un metodo lambda statico o anonimo non può acquisire le variabili locali o lo stato dell'istanza.Static lambda expressions are analogous to the static local functions: a static lambda or anonymous method can't capture local variables or instance state. Il static modificatore impedisce l'acquisizione accidentale di altre variabili.The static modifier prevents accidentally capturing other variables.

I tipi restituiti covarianti forniscono flessibilità per i tipi restituiti dei metodi di override .Covariant return types provide flexibility for the return types of override methods. Un metodo di override può restituire un tipo derivato dal tipo restituito del metodo di base sottoposto a override.An override method can return a type derived from the return type of the overridden base method. Questa operazione può essere utile per i record e per altri tipi che supportano i metodi Clone o Factory virtuali.This can be useful for records and for other types that support virtual clone or factory methods.

Inoltre, il foreach ciclo rileverà e userà un metodo di estensione GetEnumerator che altrimenti soddisfa il foreach modello.In addition, the foreach loop will recognize and use an extension method GetEnumerator that otherwise satisfies the foreach pattern. Questa modifica significa foreach coerente con altre costruzioni basate su modelli, ad esempio il modello asincrono e la decostruzione basata su modelli.This change means foreach is consistent with other pattern-based constructions such as the async pattern, and pattern-based deconstruction. In pratica, questa modifica significa che è possibile aggiungere il foreach supporto a qualsiasi tipo.In practice, this change means you can add foreach support to any type. È necessario limitarne l'utilizzo a quando l'enumerazione di un oggetto è sensata nella progettazione.You should limit its use to when enumerating an object makes sense in your design.

Successivamente, è possibile utilizzare le variabili Discard come parametri delle espressioni lambda.Next, you can use discards as parameters to lambda expressions. Questa praticità consente di evitare di denominare l'argomento e il compilatore può evitarne l'uso.This convenience enables you to avoid naming the argument, and the compiler may avoid using it. Usare _ per qualsiasi argomento.You use the _ for any argument. Per altre informazioni, vedere la sezione parametri di input di un'espressione lambda dell'articolo sulle espressioni lambda .For more information, see the Input parameters of a lambda expression section of the Lambda expressions article.

Infine, è ora possibile applicare gli attributi alle funzioni locali.Finally, you can now apply attributes to local functions. Ad esempio, è possibile applicare annotazioni di attributi Nullable alle funzioni locali.For example, you can apply nullable attribute annotations to local functions.

Supporto per i generatori di codiceSupport for code generators

Due funzionalità finali supportano i generatori di codice C#.Two final features support C# code generators. I generatori di codice C# sono un componente che è possibile scrivere che è simile a un analizzatore Roslyn o a una correzione del codice.C# code generators are a component you can write that is similar to a roslyn analyzer or code fix. La differenza è che i generatori di codice analizzano il codice e scrivono i nuovi file di codice sorgente come parte del processo di compilazione.The difference is that code generators analyze code and write new source code files as part of the compilation process. Un generatore di codice tipico esegue la ricerca di attributi o altre convenzioni nel codice.A typical code generator searches code for attributes or other conventions.

Un generatore di codice legge gli attributi o altri elementi di codice usando le API di analisi Roslyn.A code generator reads attributes or other code elements using the Roslyn analysis APIs. Da queste informazioni, aggiunge nuovo codice alla compilazione.From that information, it adds new code to the compilation. I generatori di origine possono aggiungere solo codice; non è consentito modificare il codice esistente nella compilazione.Source generators can only add code; they aren't allowed to modify any existing code in the compilation.

Le due funzionalità aggiunte per i generatori di codice sono estensioni per sintassi del metodo parziale _ e _inizializzatori del modulo*.The two features added for code generators are extensions to partial method syntax _, and _module initializers*. In primo luogo, le modifiche apportate ai metodi parziali.First, the changes to partial methods. Prima di C# 9,0, i metodi parziali sono private ma non possono specificare un modificatore di accesso, avere un valore void restituito e non possono avere out parametri.Before C# 9.0, partial methods are private but can't specify an access modifier, have a void return, and can't have out parameters. Queste restrizioni hanno significato che se non viene fornita alcuna implementazione del metodo, il compilatore rimuove tutte le chiamate al metodo parziale.These restrictions meant that if no method implementation is provided, the compiler removes all calls to the partial method. C# 9,0 rimuove queste restrizioni, ma richiede che le dichiarazioni di metodo parziale dispongano di un'implementazione.C# 9.0 removes these restrictions, but requires that partial method declarations have an implementation. I generatori di codice possono fornire tale implementazione.Code generators can provide that implementation. Per evitare di introdurre una modifica di rilievo, il compilatore considera qualsiasi metodo parziale senza un modificatore di accesso per seguire le regole precedenti.To avoid introducing a breaking change, the compiler considers any partial method without an access modifier to follow the old rules. Se il metodo parziale include il private modificatore di accesso, le nuove regole regolano tale metodo parziale.If the partial method includes the private access modifier, the new rules govern that partial method.

La seconda nuova funzionalità per i generatori di codice è _ inizializzatori di modulo *.The second new feature for code generators is _*module initializers**. Gli inizializzatori di modulo sono metodi a cui è ModuleInitializerAttribute associato l'attributo.Module initializers are methods that have the ModuleInitializerAttribute attribute attached to them. Questi metodi verranno chiamati dal runtime prima di qualsiasi altro accesso al campo o chiamata al metodo all'interno dell'intero modulo.These methods will be called by the runtime before any other field access or method invocation within the entire module. Metodo inizializzatore di modulo:A module initializer method:

  • Deve essere staticoMust be static
  • Deve essere senza parametriMust be parameterless
  • Deve restituire voidMust return void
  • Non deve essere un metodo genericoMust not be a generic method
  • Non deve essere contenuto in una classe genericaMust not be contained in a generic class
  • Deve essere accessibile dal modulo contenitoreMust be accessible from the containing module

L'ultimo punto elenco indica in modo efficace che il metodo e la classe che lo contiene devono essere interni o pubblici.That last bullet point effectively means the method and its containing class must be internal or public. Il metodo non può essere una funzione locale.The method can't be a local function.