Tipi riferimento nullable

In un contesto di tipo nullable-oblivious, tutti i tipi riferimento erano nullable. Tipi riferimento nullable fa riferimento a un gruppo di funzionalità abilitate in un contesto con riconoscimento dei valori nullable che riduce al minimo la probabilità che il codice generi System.NullReferenceException tramite il runtime. I tipi riferimento nullable includono tre funzionalità che consentono di evitare queste eccezioni, inclusa la possibilità di contrassegnare in modo esplicito un tipo di riferimento come nullable:

  • Analisi statica del flusso migliorata che determina se una variabile potrebbe essere null prima di dereferenziarla.
  • Attributi che annotano le API in modo che l'analisi del flusso determini il valore di null-state.
  • Annotazioni delle variabili usate dagli sviluppatori per dichiarare in modo esplicito il valore di null-state previsto per una variabile.

Il compilatore tiene traccia del valore di null-state per ogni espressione nel codice in fase di compilazione. null-state ha uno dei tre valori:

  • not-null: l'espressione è nota come diversa da null.
  • maybe-null: l'espressione potrebbe essere null.
  • oblivious: il compilatore non è in grado di determinare il valore di null-state dell'espressione.

Le annotazioni delle variabili determinano il supporto dei valori Null di una variabile del tipo riferimento:

  • non-nullable: se si assegna un valore null o un'espressione maybe-null alla variabile, il compilatore genera un avviso. Le variabili che sono non-nullable hanno uno stato null predefinito not-null.
  • nullable: è possibile assegnare un valore null o un'espressione maybe-null alla variabile. Quando lo stato null della variabile è maybe-null, il compilatore genera un avviso se si dereferenzia la variabile. Lo stato null predefinito per la variabile è maybe-null.
  • oblivious: è possibile assegnare un valore null o un'espressione maybe-null alla variabile. Il compilatore non genera avvisi quando si dereferenzia la variabile o quando si assegna un'espressione maybe-null alla variabile.

Lo stato null oblivious e il supporto dei valori Null oblivious corrispondono al comportamento prima dell'introduzione dei tipi riferimento nullable. Questi valori sono utili durante la migrazione o quando l'app usa una libreria che non ha abilitato i tipi riferimento nullable.

L'analisi dello stato null e le annotazioni delle variabili sono disabilitate per impostazione predefinita per i progetti esistenti, il che significa che tutti i tipi riferimento continuano a essere nullable. A partire da .NET 6, sono abilitati per impostazione predefinita per i nuovi progetti. Per informazioni sull'abilitazione di queste funzionalità con la dichiarazione di un contesto di annotazione nullable, vedere Contesti nullable.

Nella parte restante di questo articolo viene descritto il funzionamento di queste tre aree di funzionalità per generare avvisi quando il codice potrebbe dereferenziare un valore null. Dereferenziare una variabile significa accedere a uno dei suoi membri usando l'operatore . (punto), come illustrato nell'esempio seguente:

string message = "Hello, World!";
int length = message.Length; // dereferencing "message"

Quando si dereferenzia una variabile il cui valore è null, il runtime genera un'eccezione System.NullReferenceException.

Imparerai:

  • Analisi dello stato null del compilatore: in che modo il compilatore determina se un'espressione è not-null o maybe-null.
  • Attributi applicati alle API che forniscono più contesto per l'analisi dello stato null del compilatore.
  • Annotazioni di variabili nullable che forniscono informazioni sulla finalità per le variabili. Le annotazioni sono utili per i campi per impostare lo stato null predefinito all'inizio dei metodi membro.
  • Regole che regolano gli argomenti di tipo generico. Sono stati aggiunti nuovi vincoli perché i parametri di tipo possono essere tipi riferimento o tipi valore. Il suffisso ? viene implementato in modo diverso per i tipi valore nullable e i tipi riferimento nullable.
  • I contesti nullable consentono di eseguire la migrazione di progetti di grandi dimensioni. È possibile abilitare contesti o avvisi nullable in parti dell'app durante la migrazione. Dopo aver risolto molti avvisi, è possibile abilitare i tipi riferimento nullable per l'intero progetto.

Infine, si apprenderà le insidie note per l'analisi dello stato null nei tipi e nelle matrici struct.

È anche possibile esplorare questi concetti nel modulo Learn sulla sicurezza dei valori Null in C#.

Analisi dello stato null

Quando i tipi riferimento nullable sono abilitati, l'analisi dello stato null tiene traccia dello stato null dei riferimenti. Un'espressione è not-null o maybe-null. Il compilatore determina che una variabile è not-null in due modi:

  1. Alla variabile è stato assegnato un valore noto come not-null.
  2. La variabile è stata confrontata con null e dopo tale controllo non è stata modificata.

Quando i tipi riferimento nullable non sono abilitati, tutte le espressioni hanno lo stato null oblivious. Nella parte restante della sezione viene descritto il comportamento quando sono abilitati i tipi riferimento nullable.

Qualsiasi variabile che il compilatore non ha determinato come not-null è considerata maybe-null. L'analisi fornisce avvisi in situazioni in cui è possibile dereferenziare accidentalmente un valore null. Il compilatore genera avvisi in base allo stato null.

  • Quando una variabile è not-null, tale variabile potrebbe essere dereferenziata in modo sicuro.
  • Quando una variabile è maybe-null, è necessario controllare tale variabile per assicurarsi che non sia null prima di dereferenziarla.

Si consideri l'esempio seguente:

string message = null;

// warning: dereference null.
Console.WriteLine($"The length of the message is {message.Length}");

var originalMessage = message;
message = "Hello, World!";

// No warning. Analysis determined "message" is not-null.
Console.WriteLine($"The length of the message is {message.Length}");

// warning!
Console.WriteLine(originalMessage.Length);

Nell'esempio precedente il compilatore determina che message è maybe-null quando viene stampato il primo messaggio. Non viene visualizzato alcun avviso per il secondo messaggio. La riga finale di codice genera un avviso perché originalMessage potrebbe essere null. L'esempio seguente illustra un uso più pratico per attraversare un albero di nodi alla radice, elaborando ogni nodo durante l'attraversamento:

void FindRoot(Node node, Action<Node> processNode)
{
    for (var current = node; current != null; current = current.Parent)
    {
        processNode(current);
    }
}

Il codice precedente non genera avvisi per la dereferenziazione della variabile current. L'analisi statica determina che current non viene mai dereferenziata quando è maybe-null. La variabile current viene confrontata con null prima dell'accesso a current.Parent e prima di passare current all'azione ProcessNode. Negli esempi precedenti viene illustrato come il compilatore determina lo stato null per le variabili locali durante l'inizializzazione, l'assegnazione o il confronto con null.

L'analisi dello stato null non viene tracciata nei metodi chiamati. Di conseguenza, i campi inizializzati in un metodo helper comune chiamato da tutti i costruttori generano un avviso con il modello seguente:

La proprietà non-nullable "name"deve contenere un valore non-null quando si esce dal costruttore.

È possibile risolvere questi avvisi in uno dei due modi seguenti: concatenamento dei costruttori o attributi nullable nel metodo helper. Il codice seguente visualizza un esempio con ogni metodo. La classe Person usa un costruttore comune chiamato da tutti gli altri costruttori. La classe Student ha un metodo helper annotato con l'attributo System.Diagnostics.CodeAnalysis.MemberNotNullAttribute:


using System.Diagnostics.CodeAnalysis;

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public Person(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName = lastName;
    }

    public Person() : this("John", "Doe") { }
}

public class Student : Person
{
    public string Major { get; set; }

    public Student(string firstName, string lastName, string major)
        : base(firstName, lastName)
    {
        SetMajor(major);
    }

    public Student(string firstName, string lastName) :
        base(firstName, lastName)
    {
        SetMajor();
    }

    public Student()
    {
        SetMajor();
    }

    [MemberNotNull(nameof(Major))]
    private void SetMajor(string? major = default)
    {
        Major = major ?? "Undeclared";
    }
}

Nota

In C# 10 sono stati aggiunti numerosi miglioramenti all'assegnazione definita e all'analisi dello stato null. Quando si esegue l'aggiornamento a C# 10, è possibile che vengano visualizzati meno avvisi nullable che sono falsi positivi. Altre informazioni sui miglioramenti sono disponibili nella specifica delle funzionalità per i miglioramenti delle assegnazioni definite.

L'analisi dello stato nullable e gli avvisi generati dal compilatore consentono di evitare errori del programma dereferenziando null. L'articolo sulla risoluzione degli avvisi nullable fornisce tecniche per correggere gli avvisi più probabilmente visualizzati nel codice.

Attributi per le firme API

L'analisi dello stato null richiede suggerimenti da parte degli sviluppatori per comprendere la semantica delle API. Alcune API forniscono controlli null e devono modificare lo stato null di una variabile da maybe-null a not-null. Altre API restituiscono espressioni che sono not-null o maybe-null a seconda dello stato null degli argomenti di input. Si consideri ad esempio il codice seguente che visualizza un messaggio in lettere maiuscole:

void PrintMessageUpper(string? message)
{
    if (!IsNull(message))
    {
        Console.WriteLine($"{DateTime.Now}: {message.ToUpper()}");
    }
}

bool IsNull(string? s) => s == null;

In base all'analisi, qualsiasi sviluppatore considererebbe questo codice sicuro e non dovrebbe generare avvisi. Tuttavia, il compilatore non sa che IsNull fornisce un controllo null e genera un avviso per l'istruzione message.ToUpper(), considerando message come una variabile maybe-null. Usare l'attributo NotNullWhen per correggere questo avviso:

bool IsNull([NotNullWhen(false)] string? s) => s == null;

Questo attributo informa il compilatore che, se IsNull restituisce false, il parametro s non è null. Il compilatore modifica lo stato null di message in not-null all'interno del blocco if (!IsNull(message)) {...}. Non viene generato alcun avviso.

Gli attributi forniscono informazioni dettagliate sullo stato null degli argomenti, dei valori restituiti e dei membri dell'istanza dell'oggetto utilizzato per richiamare un membro. I dettagli su ogni attributo sono disponibili nell'articolo di riferimento sul linguaggio sugli attributi del riferimento nullable. A partire da .NET 5, tutte le API del runtime .NET vengono annotate. È possibile migliorare l'analisi statica annotando le API per fornire informazioni semantiche sullo stato null degli argomenti e sui valori restituiti.

Annotazioni delle variabili nullable

L'analisi dello stato null fornisce un'analisi affidabile per le variabili locali. Il compilatore necessita di altre informazioni per le variabili membro. Il compilatore necessita di altre informazioni per impostare lo stato null di tutti i campi nella parentesi aperta di un membro. È possibile utilizzare uno dei costruttori accessibili per inizializzare l'oggetto. Se fosse possibile impostare un campo membro su null, il compilatore dovrebbe presupporre che lo stato null sia maybe-null all'inizio di ogni metodo.

Si usano annotazioni che possono dichiarare se una variabile è un tipo riferimento nullable o un tipo riferimento non-nullable. Queste annotazioni creano istruzioni importanti sullo stato null per le variabili:

  • Non si presume che un riferimento sia null. Lo stato predefinito di una variabile di riferimento non-nullable è not-null. Il compilatore applica regole che garantiscono che sia sicuro dereferenziare queste variabili senza prima controllare che non siano null:
    • La variabile deve essere inizializzata come valore non Null.
    • Alla variabile non può mai essere assegnato il valore null. Il compilatore genera un avviso quando il codice assegna un'espressione maybe-null a una variabile che non deve essere null.
  • Un riferimento può essere null. Lo stato predefinito di una variabile di riferimento nullable è maybe-null. Il compilatore applica le regole per assicurarsi di controllare correttamente la presenza di un riferimento null:
    • La variabile può essere dereferenziata solo quando il compilatore può garantire che il valore non sia null.
    • Queste variabili possono essere inizializzate con il valore predefinito null e possono ricevere l'assegnazione del valore null in un altro codice.
    • Il compilatore non genera avvisi quando il codice assegna un'espressione maybe-null a una variabile che potrebbe essere null.

Qualsiasi variabile di riferimento non-nullable ha uno stato null predefinito not-null. Qualsiasi variabile di riferimento nullable ha lo stato null iniziale maybe-null.

Viene inserita una nota in un tipo riferimento nullable usando la stessa sintassi dei tipi valore nullable: viene aggiunto ? al tipo della variabile. La dichiarazione della variabile seguente, ad esempio, rappresenta una variabile di stringa nullable, name:

string? name;

Quando i tipi riferimento nullable sono abilitati, qualsiasi variabile in cui ? non viene aggiunto al nome del tipo è un tipo riferimento non-nullable. Ciò include tutte le variabili dei tipi riferimento nel codice esistente quando si abilita questa funzionalità. Tuttavia, le variabili locali tipizzate in modo implicito (dichiarate tramite var) sono tipi riferimento nullable. Come illustrato nelle sezioni precedenti, l'analisi statica determina lo stato null delle variabili locali per determinare se sono maybe-null prima di dereferenziarle.

In alcuni casi è necessario eseguire l'override di un avviso quando si sa che una variabile non è null, ma il compilatore determina che il relativo stato null è maybe-null. L'operatore null-forgiving! viene usato dopo un nome di variabile per fare in modo che lo stato null sia not-null. Se ad esempio si è certi che la variabile name non è null, ma il compilatore genera un avviso, è possibile scrivere il codice seguente per eseguire l'override dell'analisi del compilatore:

name!.Length;

I tipi riferimento nullable e i tipi valore nullable forniscono un concetto semantico simile: una variabile può rappresentare un valore o un oggetto oppure tale variabile potrebbe essere null. Tuttavia, i tipi riferimento nullable e i tipi valore nullable vengono implementati in modo diverso: i tipi valore nullable vengono implementati usando System.Nullable<T> e i tipi riferimento nullable vengono implementati dagli attributi letti dal compilatore. Ad esempio, string? e string sono entrambi rappresentati dallo stesso tipo: System.String. Tuttavia, int? e int sono rappresentati rispettivamente da System.Nullable<System.Int32> e System.Int32.

I tipi riferimento nullable sono una funzionalità della fase di compilazione. Ciò significa che è possibile per i chiamanti ignorare gli avvisi, usare null intenzionalmente come argomento per un metodo che prevede un riferimento non nullable. Gli autori di librerie devono includere controlli di runtime rispetto ai valori degli argomenti null. ArgumentNullException.ThrowIfNull è l'opzione preferita per controllare un parametro rispetto a null in fase di esecuzione.

Importante

L'abilitazione di annotazioni nullable può modificare il modo in cui Entity Framework Core determina se è necessario un membro dati. Per altre informazioni, vedere l'articolo sui concetti fondamentali di Entity Framework Core: uso dei tipi riferimento nullable.

Generics

La funzionalità generics richiede regole dettagliate per gestire T? per qualsiasi parametro di tipo T. Le regole sono necessariamente dettagliate a causa della cronologia e dell'implementazione diversa per un tipo valore nullable e un tipo riferimento nullable. I tipi valore nullable vengono implementati usando lo struct System.Nullable<T>. I tipi riferimento nullable vengono implementati come annotazioni di tipo che forniscono regole semantiche al compilatore.

  • Se l'argomento tipo per T è un tipo riferimento, T? fa riferimento al tipo riferimento nullable corrispondente. Ad esempio, se T è un valore string, allora T? è un valore string?.
  • Se l'argomento tipo per T è un tipo valore, T? fa riferimento allo stesso tipo valore, T. Ad esempio, se T è un valore int, anche T? è un valore int.
  • Se l'argomento tipo per T è un tipo riferimento nullable, T? fa riferimento allo stesso tipo riferimento nullable. Ad esempio, se T è un valore string?, allora anche T? è un valore string?.
  • Se l'argomento tipo per T è un tipo valore nullable, T? fa riferimento allo stesso tipo valore nullable. Ad esempio, se T è un valore int?, allora anche T? è un valore int?.

Per i valori restituiti, T? equivale a [MaybeNull]T. Per i valori degli argomenti, T? equivale a [AllowNull]T. Per altre informazioni, vedere l'articolo sugli attributi per l'analisi dello stato null nel riferimento al linguaggio.

È possibile specificare un comportamento diverso usando i vincoli:

  • Il vincolo class significa che T deve essere un tipo riferimento non-nullable, ad esempio string. Il compilatore genera un avviso se si usa un tipo riferimento nullable, ad esempio string? per T.
  • Il vincolo class? significa che T deve essere un tipo riferimento, un tipo riferimento non-nullable (string) o nullable (ad esempio string?). Quando il parametro di tipo è un tipo riferimento nullable, ad esempio string?, un'espressione di T? fa riferimento allo stesso tipo riferimento nullable, ad esempio string?.
  • Il vincolo notnull significa che T deve essere un tipo riferimento non-nullable o un tipo valore non-nullable. Se si usa un tipo riferimento nullable o un tipo valore nullable per il parametro di tipo, il compilatore genera un avviso. Inoltre, quando T è un tipo valore, il valore restituito è quel tipo di valore, non il tipo valore nullable corrispondente.

Questi vincoli consentono di fornire altre informazioni al compilatore sulla modalità in viene usato T. Ciò è utile quando gli sviluppatori devono scegliere il tipo per T e fornisce un'analisi migliore dello stato null quando viene usata un'istanza del tipo generico.

Contesti nullable

Per i progetti di piccole dimensioni, è possibile abilitare tipi di riferimento nullable, correggere gli avvisi e continuare. Tuttavia, per progetti di grandi dimensioni e soluzioni multiprogetto, ciò potrebbe generare un numero elevato di avvisi. È possibile usare i pragma per abilitare i tipi riferimento nullable file per file quando si inizia a usare i tipi riferimento nullable. Le nuove funzionalità che proteggono dalla generazione di un'eccezione System.NullReferenceException possono comportare interruzioni quando sono attivate in una codebase esistente:

  • Tutte le variabili di riferimento tipizzate in modo esplicito vengono interpretate come tipi riferimento non-nullable.
  • Il significato del vincolo class in generics è cambiato in modo da indicare un tipo riferimento non-nullable.
  • I nuovi avvisi vengono generati a causa di queste nuove regole.

Il contesto di annotazione nullable determina il comportamento del compilatore. Esistono quattro valori per il contesto di annotazione nullable:

  • disable: il codice è nullable-oblivious. Disable corrisponde al comportamento prima dell'abilitazione dei tipi riferimento nullable, ad eccezione del fatto che la nuova sintassi genera avvisi anziché errori.
    • Gli avvisi nullable sono disabilitati.
    • Tutte le variabili di tipo riferimento sono tipi riferimento nullable.
    • L'uso del suffisso ? per dichiarare un tipo riferimento nullable genera un avviso.
    • È possibile usare l'operatore null-forgiving, !, ma non ha alcun effetto.
  • enable: il compilatore abilita tutte le funzionalità del linguaggio e l'analisi dei riferimenti null.
    • Tutti i nuovi avvisi nullable sono abilitati.
    • È possibile usare il suffisso ? per dichiarare un tipo riferimento nullable.
    • Le variabili di tipo riferimento senza il suffisso ? sono tipi riferimento non-nullable.
    • L'operatore null-forgiving elimina gli avvisi per un'assegnazione possibile a null.
  • warnings: il compilatore esegue tutte le analisi null e genera avvisi quando il codice potrebbe dereferenziare null.
    • Tutti i nuovi avvisi nullable sono abilitati.
    • L'uso del suffisso ? per dichiarare un tipo riferimento nullable genera un avviso.
    • Tutte le variabili di tipo riferimento possono essere null. Tuttavia, i membri hanno lo stato nullnot-null alla parentesi aperta di tutti i metodi, a meno che non sia dichiarato con il suffisso ?.
    • È possibile usare l'operatore null-forgiving !.
  • annotations: il compilatore non genera avvisi quando il codice potrebbe dereferenziare nullo quando si assegna un'espressione maybe-null a una variabile non-nullable.
    • Tutti gli avvisi nullable sono disabilitati.
    • È possibile usare il suffisso ? per dichiarare un tipo riferimento nullable.
    • Le variabili di tipo riferimento senza il suffisso ? sono tipi riferimento non-nullable.
    • È possibile usare l'operatore null-forgiving, !, ma non ha alcun effetto.

Il contesto dell'annotazione nullable e il contesto dell'avviso nullable possono essere impostati per un progetto usando l'elemento <Nullable> nel file .csproj. Questo elemento configura come il compilatore interpreta il supporto dei valori null dei tipi e quali avvisi vengono generati. La tabella seguente mostra i valori consentiti e riepiloga i contesti specificati.

Contesto Avvisi di dereferenziazione Avvisi di assegnazione Tipi riferimento Suffisso ? Operatore !
disable Disattivato Disabled Sono tutti nullable Genera un avviso Non ha effetti
enable Attivata Attivata Non-nullable a meno che non sia dichiarato con ? Dichiara un tipo nullable Elimina gli avvisi per l'eventuale assegnazione null
warnings Attivata Non applicabile Tutti sono nullable, ma i membri vengono considerati not-null alla parentesi aperta dei metodi Genera un avviso Elimina gli avvisi per l'eventuale assegnazione null
annotations Disattivato Disabled Non-nullable a meno che non sia dichiarato con ? Dichiara un tipo nullable Non ha effetti

Le variabili di tipo riferimento nel codice compilato in un contesto disabled sono nullable-oblivious. È possibile assegnare un valore letterale null o una variabile maybe-null a una variabile che sia nullable-oblivious. Tuttavia, lo stato predefinito di una variabile nullable-oblivious è not-null.

È possibile scegliere quale impostazione è migliore per il progetto:

  • Scegliere disable per i progetti legacy che non si vuole aggiornare in base alla diagnostica o alle nuove funzionalità.
  • Scegliere warnings per determinare dove il codice potrebbe generare eccezioni System.NullReferenceException. È possibile risolvere questi avvisi prima di modificare il codice per abilitare i tipi riferimento non-nullable.
  • Scegliere annotations per esprimere la finalità di progettazione prima di abilitare gli avvisi.
  • Scegliere enable per i nuovi progetti e i progetti attivi in cui ci si vuole proteggere da eccezioni di riferimento null.

Esempio:

<Nullable>enable</Nullable>

È anche possibile usare le direttive per impostare questi stessi contesti ovunque nel codice sorgente. Le direttive sono particolarmente utili quando si esegue la migrazione di una codebase di grandi dimensioni.

  • #nullable enable: imposta il contesto dell'annotazione nullable e il contesto dell'avviso nullable su enable.
  • #nullable disable: imposta il contesto dell'annotazione nullable e il contesto dell'avviso nullable su disable.
  • #nullable restore: ripristina le impostazioni di progetto per il contesto dell'annotazione nullable e il contesto dell'avviso nullable.
  • #nullable disable warnings: imposta il contesto dell'avviso nullable su disable.
  • #nullable enable warnings: imposta il contesto dell'avviso nullable su enable.
  • #nullable restore warnings: ripristina le impostazioni di progetto per il contesto dell'avviso nullable.
  • #nullable disable annotations: imposta il contesto dell'annotazione nullable su disable.
  • #nullable enable annotations: imposta il contesto dell'annotazione nullable su enable.
  • #nullable restore annotations: ripristina le impostazioni di progetto per il contesto dell'avviso nullable.

Per qualsiasi riga di codice, è possibile impostare una delle combinazioni seguenti:

Contesto di avviso Contesto di annotazione Utilizzo
impostazioni predefinite del progetto impostazioni predefinite del progetto Default
abilitazione disable Correggere gli avvisi di analisi
abilitazione impostazioni predefinite del progetto Correggere gli avvisi di analisi
impostazioni predefinite del progetto abilitazione Aggiungere annotazioni del tipo
abilitazione abilitazione Migrazione già eseguita per il codice
disable abilitazione Annotare il codice prima di correggere gli avvisi
disabilitare disable Aggiunta di codice legacy al progetto di cui è stata seguita la migrazione
impostazioni predefinite del progetto disable Raramente
disable impostazioni predefinite del progetto Raramente

Queste nove combinazioni forniscono un controllo con granularità fine sulla diagnostica che il compilatore genera per il codice. È possibile abilitare altre funzionalità in qualsiasi area che l'utente sta aggiornando, senza visualizzare altri avvisi che non è ancora pronto a risolvere.

Importante

Il contesto che ammette i valori Null globale non si applica ai file di codice generati. Qualsiasi strategia si usi, il contesto nullable è disabilitato per qualsiasi file di origine contrassegnato come generato. Ciò significa che le API nei file generati non vengono annotate. Esistono quattro modi in cui un file viene contrassegnato come generato:

  1. In .editorconfig specificare generated_code = true in una sezione che si applica a tale file.
  2. Inserire <auto-generated> o <auto-generated/> in un commento all'inizio del file. Può trovarsi in qualsiasi riga di tale commento, ma il blocco di commento deve essere il primo elemento del file.
  3. Iniziare il nome del file con TemporaryGeneratedFile_
  4. Terminare il nome del file con .designer.cs, .generated.cs, .g.cs o .g.i.cs.

I generatori possono acconsentire esplicitamente usando la direttiva del preprocessore #nullable.

Per impostazione predefinita, i contesti dell'annotazione e dell'avviso nullable sono disabilitati. Ciò significa che il codice esistente viene compilato senza modifiche e senza generare nuovi avvisi. A partire da .NET 6, i nuovi progetti includono l'elemento <Nullable>enable</Nullable> in tutti i modelli del progetto.

Queste opzioni forniscono due strategie distinte per aggiornare una codebase esistente per l'uso di tipi riferimento nullable.

Problemi noti

Le matrici e gli struct che contengono tipi riferimento sono problemi noti nei riferimenti nullable e nell'analisi statica che determina la sicurezza null. In entrambe le situazioni, è possibile inizializzare un riferimento non-nullable su null, senza generare avvisi.

Struct

Uno struct che contiene tipi riferimento non-nullable consente di assegnare default senza avvisi. Si consideri l'esempio seguente:

using System;

#nullable enable

public struct Student
{
    public string FirstName;
    public string? MiddleName;
    public string LastName;
}

public static class Program
{
    public static void PrintStudent(Student student)
    {
        Console.WriteLine($"First name: {student.FirstName.ToUpper()}");
        Console.WriteLine($"Middle name: {student.MiddleName?.ToUpper()}");
        Console.WriteLine($"Last name: {student.LastName.ToUpper()}");
    }

    public static void Main() => PrintStudent(default);
}

Nell'esempio precedente non viene visualizzato alcun avviso in PrintStudent(default) mentre i tipi riferimento non-nullable FirstName e LastName sono null.

Un altro caso più comune è quando si gestiscono struct generici. Si consideri l'esempio seguente:

#nullable enable

public struct S<T>
{
    public T Prop { get; set; }
}

public static class Program
{
    public static void Main()
    {
        string s = default(S<string>).Prop;
    }
}

Nell'esempio precedente la proprietà Prop è null in fase di esecuzione. Viene assegnata a una stringa non-nullable senza alcun avviso.

Matrici

Anche le matrici sono un problema noto nei tipi riferimento nullable. Si consideri l'esempio seguente che non genera avvisi:

using System;

#nullable enable

public static class Program
{
    public static void Main()
    {
        string[] values = new string[10];
        string s = values[0];
        Console.WriteLine(s.ToUpper());
    }
}

Nell'esempio precedente la dichiarazione della matrice mostra che questa contiene stringhe non-nullable, mentre i relativi elementi vengono tutti inizializzati in null. Alla variabile s viene quindi assegnato un valore null (il primo elemento della matrice). Infine, la variabile s viene dereferenziata causando un'eccezione del runtime.

Vedi anche