Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
tipi riferimento Nullable sono un gruppo di funzionalità che riducono al minimo la probabilità che il codice generi il runtime System.NullReferenceException. 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
nullprima 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. Il stato nullo ha uno dei due valori seguenti:
-
not-null: l'espressione è nota per non essere
null. -
maybe-null: l'espressione potrebbe essere
null.
Le annotazioni delle variabili determinano la nullabilità di una variabile del tipo di riferimento:
-
non-nullable: se si assegna un valore
nullo 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
nullo 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.
Nella parte restante di questo articolo viene descritto come funzionano queste tre aree di funzionalità per produrre 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.
Analogamente, gli avvisi possono essere generati quando [] viene usata la notazione per accedere a un membro di un oggetto quando l'oggetto è null:
using System;
public class Collection<T>
{
private T[] array = new T[100];
public T this[int index]
{
get => array[index];
set => array[index] = value;
}
}
public static void Main()
{
Collection<int> c = default;
c[10] = 1; // CS8602: Possible dereference of null
}
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 annullabili che forniscono informazioni sulla tua intenzione per le variabili. Le annotazioni sono utili per campi, parametri e valori restituiti per impostare lo stato Null predefinito.
- 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 di valore nullable e i tipi di riferimento nullable. - Il contesto nullable consente di eseguire la migrazione di progetti di grandi dimensioni. È possibile abilitare avvisi e annotazioni nel contesto nullable in parti dell'app durante la migrazione. Dopo aver gestito più avvisi, è possibile abilitare entrambe le impostazioni 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
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:
- Alla variabile è stato assegnato un valore noto come non null.
- La variabile è stata controllata in base al
nulle non è stata assegnata dopo tale controllo.
Qualsiasi variabile che il compilatore non può determinare come non Null viene considerata forse 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 è non-null, tale variabile può essere dereferenziata in modo sicuro.
- Quando una variabile è maybe-null, è necessario controllare tale variabile per assicurarsi che non sia
nullprima 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 esamina i metodi chiamati. Di conseguenza, i campi inizializzati in un metodo helper comune chiamato da tutti i costruttori potrebbero generare un avviso con il messaggio seguente:
La proprietà non-nullable "name"deve contenere un valore non-null quando si esce dal costruttore.
È possibile risolvere queste segnalazioni in uno dei due modi seguenti: concatenamento dei costruttori o attributi annullabili sul 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";
}
}
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 di nullabilità fornisce tecniche per correggere gli avvisi più comuni nel tuo codice. Le diagnostiche generate dall'analisi dello stato Null sono soltanto avvisi.
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 in base allo 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)) {...}. Nessun avvertimento viene generato.
Gli attributi forniscono informazioni dettagliate sullo stato null degli argomenti, dei valori restituiti e dei membri dell'oggetto usato per invocare 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 annullabili
L'analisi dello stato null fornisce un'analisi affidabile per le variabili locali. Il compilatore ha bisogno di ulteriori 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 di riferimento annullabile o un tipo di riferimento non annullabile. 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
nullpredefinito e possono essere assegnate al valorenullin altro codice. - Il compilatore non genera avvisi quando il codice assegna un'espressione maybe-null a una variabile che potrebbe essere null.
- La variabile può essere dereferenziata solo quando il compilatore può garantire che il valore non sia
Qualsiasi variabile di riferimento non annullabile ha il null-state iniziale di non null. Qualsiasi variabile di riferimento nullable ha lo stato null iniziale maybe-null.
Un tipo di riferimento nullable è indicato utilizzando la stessa sintassi dei tipi di valore nullable: un ? è aggiunto al tipo della variabile. La dichiarazione della variabile seguente, ad esempio, rappresenta una variabile di stringa nullable, name:
string? name;
Quando i tipi di riferimento nullable sono abilitati, qualsiasi variabile in cui ? non è presente nel nome del tipo è un tipo di riferimento non-nullable. Ciò include tutte le variabili dei tipi riferimento nel codice esistente quando si abilita questa funzionalità. Tuttavia, le variabili locali dichiarate in modo implicito (tramite var) sono tipi di 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 nullo sia non nullo. 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 per i valori degli argomenti null.
ArgumentNullException.ThrowIfNull è l'opzione preferita per controllare un parametro rispetto a null in fase di esecuzione. Inoltre, il comportamento di runtime di un programma che utilizza annotazioni annullabili è lo stesso se vengono rimosse tutte le annotazioni annullabili, (? e !). Il loro unico scopo è esprimere la finalità di progettazione e fornire informazioni per l'analisi dello stato Null.
Importante
L'abilitazione delle annotazioni di nullabilità può modificare il modo in cui Entity Framework Core determina se un membro dati è necessario. Per ulteriori dettagli, consultare l'articolo sui fondamenti di Entity Framework Core: lavorare con i tipi di riferimento nullable.
Generici
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 di valore nullable e un tipo di riferimento nullable. I tipi valore nullable vengono implementati usando lo struct System.Nullable<T>. I tipi di 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, seTè un valorestring, alloraT?è un valorestring?. - Se l'argomento tipo per
Tè un tipo valore,T?fa riferimento allo stesso tipo valore,T. Ad esempio, seTè un valoreint, ancheT?è un valoreint. - Se l'argomento tipo per
Tè un tipo riferimento nullable,T?fa riferimento allo stesso tipo riferimento nullable. Ad esempio, seTè un valorestring?, allora ancheT?è un valorestring?. - Se l'argomento tipo per
Tè un tipo valore nullable,T?fa riferimento allo stesso tipo valore nullable. Ad esempio, seTè un valoreint?, allora ancheT?è un valoreint?.
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
classsignifica cheTdeve essere un tipo riferimento non-nullable, ad esempiostring. Il compilatore genera un avviso se si usa un tipo riferimento nullable, ad esempiostring?perT. - Il vincolo
class?significa cheTdeve essere un tipo riferimento, un tipo riferimento non-nullable (string) o nullable (ad esempiostring?). Quando il parametro di tipo è un tipo riferimento nullable, ad esempiostring?, un'espressione diT?fa riferimento allo stesso tipo riferimento nullable, ad esempiostring?. - Il vincolo
notnullsignifica cheTdeve essere un tipo di riferimento non annullabile o un tipo di valore non annullabile. Se utilizzi un tipo di riferimento nullable o un tipo di valore nullable per il parametro di tipo, il compilatore emette un avviso. Inoltre, quandoTè 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.
Contesto nullable
Il contesto annullabile determina come vengono gestite le annotazioni dei tipi di riferimento annullabile e quali avvisi vengono generati dall'analisi statica dello stato di null. Il contesto nullable contiene due flag: l'impostazione di annotazione e l'impostazione di avviso .
Sia le impostazioni dell'annotazione che quelle di avviso sono disattivate di default per i progetti esistenti. A partire da .NET 6 (C# 10), per i nuovi progetti , entrambi i flag sono abilitati per impostazione predefinita. Il motivo di avere due flag distinti per il contesto di tipo nullable è semplificare la migrazione di progetti di grandi dimensioni che precedono l'introduzione di tipi di riferimento 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 di riferimento annullabili per ogni file man mano che si inizia a usarli. 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 di riferimento non annullabili.
- Il significato del vincolo
classin 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 combinazioni per le impostazioni del contesto annullabile :
-
entrambi disabilitati: 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 di riferimento sono tipi di 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.
-
tutte e due le opzioni sono abilitate: il compilatore abilita tutte le analisi dei riferimenti null e tutte le funzionalità del linguaggio.
- Tutti i nuovi avvisi di annullabilità (nullable) sono abilitati.
- È possibile usare il suffisso
?per dichiarare un tipo riferimento nullable. - Le variabili di riferimento di tipo senza il suffisso
?sono riferimenti non annullabili. - L'operatore null-safe sopprime gli avvisi per una possibile dereferenziazione di
null.
-
avviso abilitato: 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 null di not-null all'apertura di tutte le parentesi dei metodi, a meno che non siano dichiarati con il suffisso
?. - È possibile usare l'operatore null-forgiving
!.
-
annotazioni abilitate: Il compilatore non genera avvisi quando il codice potrebbe dereferenziare
nullo quando si assegna un'espressione potenzialmente nulla a una variabile non annullabile.- Tutti gli avvisi nullable sono disabilitati.
- È possibile usare il suffisso
?per dichiarare un tipo riferimento nullable. - Le variabili di tipo di riferimento senza il suffisso
?sono tipi di riferimento non nullable. - È possibile usare l'operatore null-forgiving,
!, ma non ha alcun effetto.
Il contesto dell'annotazione di tipo nullable e il contesto dell'avviso nullable possono essere impostati per un progetto utilizzando l'elemento <Nullable> nel file .csproj. Questo elemento configura come il compilatore interpreta la nullabilità 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 | Disabilitato | Sono tutti annullabili | Genera un avviso | Non ha effetti |
enable |
Attivata | Attivata | Non-nullable, a meno che 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 | Disattivato | 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 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 flag ovunque nel codice sorgente. Le direttive sono particolarmente utili quando si esegue la migrazione di una codebase di grandi dimensioni.
-
#nullable enable: imposta i flag di annotazione e avviso su abilitare. -
#nullable disable: imposta i flag di annotazione e avviso su per disabilitare. -
#nullable restore: ripristina il flag di annotazione e il flag di avviso nelle impostazioni del progetto. -
#nullable disable warnings: Imposta il flag di avviso su per disabilitare. -
#nullable enable warnings: impostare il flag di avviso su per abilitare. -
#nullable restore warnings: ripristina l'indicatore di avviso nelle impostazioni del progetto. -
#nullable disable annotations: impostare il flag di annotazione su disabilitare. -
#nullable enable annotations: impostare il flag di annotazione su abilitare. -
#nullable restore annotations: ripristina il flag di annotazione nelle impostazioni del progetto.
Per qualsiasi riga di codice, è possibile impostare una delle combinazioni seguenti:
| Bandiera di avvertimento | Indicatore di annotazione | Utilizzo |
|---|---|---|
| impostazioni predefinite del progetto | impostazioni predefinite del progetto | Impostazione predefinita |
| abilitare | disabilitare | Correggere gli avvisi di analisi |
| abilitare | impostazioni predefinite del progetto | Correggere gli avvisi di analisi |
| impostazioni predefinite del progetto | abilitare | Aggiungere annotazioni di tipo |
| abilitare | abilitare | Migrazione del codice già eseguita |
| disabilitare | abilitare | Annotare il codice prima di correggere gli avvisi |
| disabilitare | disabilitare | Aggiunta di codice legacy al progetto migrato |
| impostazioni predefinite del progetto | disabilitare | Raramente |
| disabilitare | 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 globale che ammette valori Null non si applica ai file di codice generati. In entrambe le strategie, il contesto nullable è disabilitato per qualsiasi file di origine contrassegnato come generato. Ciò significa che le API nei file generati non vengono annotate. Non vengono generati avvisi nullable per i file generati. Esistono quattro modi in cui un file viene contrassegnato come generato:
- In .editorconfig specificare
generated_code = truein una sezione che si applica a tale file. - 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. - Iniziare il nome del file con TemporaryGeneratedFile_
- 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, le annotazioni e i flag di avviso nullable vengono 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 di progetto, impostando questi flag su abilitati.
Queste opzioni forniscono due strategie distinte per aggiornare una codebase esistente per l'uso di tipi riferimento nullable.
Problemi noti
Le matrici e le strutture che contengono tipi di riferimento sono problemi noti nei riferimenti annullabili e nell'analisi statica che determina la sicurezza dei valori null. In entrambe le situazioni, è possibile inizializzare un riferimento non-nullable su null, senza generare avvisi.
Struct
Una struct che contiene tipi di riferimento non-nullable consente di assegnare default senza generare 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) sebbene i tipi di riferimento non annullabili FirstName e LastName siano 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 alcuna segnalazione.
Matrici
Anche le matrici sono un problema noto nei tipi di riferimento nullabili. 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.
Costruttori
Il costruttore di una classe continuerà a chiamare il finalizzatore, anche se viene sollevata un'eccezione da tale costruttore.
Nell'esempio seguente viene illustrato il comportamento:
public class A
{
private string _name;
private B _b;
public A(string name)
{
ArgumentNullException.ThrowIfNullOrEmpty(name);
_name = name;
_b = new B();
}
~A()
{
Dispose();
}
public void Dispose()
{
_b.Dispose();
GC.SuppressFinalize(this);
}
}
public class B: IDisposable
{
public void Dispose() { }
}
public void Main()
{
var a = new A(string.Empty);
}
Nell'esempio precedente, l'oggetto System.NullReferenceException verrà generato quando _b.Dispose(); verrà eseguito, se il parametro name era null. La chiamata a _b.Dispose(); non solleverà mai eccezioni quando il costruttore è completato correttamente. Tuttavia, non viene generato alcun avviso dal compilatore, perché l'analisi statica non è in grado di determinare se un metodo (ad esempio un costruttore) viene completato senza che venga generata un'eccezione di runtime.