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.
I tipi di riferimento nullable integrano i tipi di riferimento allo stesso modo in cui i tipi di valore nullable integrano i tipi di valore. Per dichiarare una variabile come tipo di riferimento nullable, aggiungere un ?
al tipo. Ad esempio, string?
rappresenta un string
nullabile. È possibile usare questi nuovi tipi per esprimere in modo più chiaro la finalità di progettazione: alcune variabili devono avere sempre un valore, altre potrebbero non avere un valore.
In questa esercitazione si apprenderà come:
- Incorporare tipi di riferimento nullable e non-nullable nei design
- Abilitare i controlli dei tipi di riferimento nullable in tutto il codice.
- Scrivere codice in cui il compilatore applica tali decisioni di progettazione.
- Usare la funzionalità di riferimento nullable nelle proprie progettazioni
Prerequisiti
Per questa esercitazione si presuppone che l'utente abbia familiarità con C# e .NET, inclusa l'interfaccia della riga di comando di Visual Studio o di .NET.
Incorpora tipi di riferimento nullabili nei tuoi progetti
In questa esercitazione si creerà una libreria che modella l'esecuzione di un sondaggio. Il codice usa sia tipi di riferimento nullable che tipi di riferimento non nullable per rappresentare i concetti del mondo reale. Le domande del sondaggio non possono mai essere vuote. Un intervistato potrebbe preferire di non rispondere a una domanda. Le risposte potrebbero essere null
in questo caso.
Il codice che verrà scritto per questo esempio esprime tale finalità e il compilatore applica tale finalità.
Creare l'applicazione e abilitare i tipi di riferimento nullable
Creare una nuova applicazione console in Visual Studio o dalla riga di comando usando dotnet new console
. Assegna all'applicazione NullableIntroduction
un nome. Dopo aver creato l'applicazione, è necessario specificare che l'intero progetto viene compilato in un contesto di annotazione nullable abilitato. Aprire il file con estensione csproj e aggiungere un Nullable
elemento all'elemento PropertyGroup
. Impostarne il valore su enable
. È necessario abilitare l'opzione dei tipi di riferimento nullable nei progetti precedenti alla versione C# 11. Ciò è dovuto al fatto che una volta attivata la funzionalità, le dichiarazioni di variabili di riferimento esistenti diventano tipi di riferimento non-nullabili. Sebbene questa decisione consenta di individuare i problemi in cui il codice esistente potrebbe non avere controlli Null appropriati, potrebbe non riflettere accuratamente la finalità di progettazione originale:
<Nullable>enable</Nullable>
Prima di .NET 6, i nuovi progetti non includono l'elemento Nullable
. A partire da .NET 6, i nuovi progetti includono l'elemento <Nullable>enable</Nullable>
nel file di progetto.
Progettare i tipi per l'applicazione
Questa applicazione di sondaggio richiede la creazione di una serie di classi:
- Una classe che modella l'elenco delle domande.
- Classe che modella un elenco di persone contattate per il sondaggio.
- Una classe che modella le risposte di una persona che ha partecipato al sondaggio.
Questi tipi useranno sia i tipi di riferimento nullable che quelli non nullable per esprimere quali membri sono necessari e quali membri sono facoltativi. I tipi di riferimento nullable comunicano chiaramente la finalità di progettazione:
- Le domande che fanno parte del sondaggio non possono mai essere null: non ha senso porre una domanda vuota.
- Gli intervistati non possono mai essere null. Si vuole tenere traccia delle persone contattate, anche gli intervistati che hanno rifiutato di partecipare.
- Qualsiasi risposta a una domanda può essere nulla. Gli intervistati possono rifiutare di rispondere ad alcune o a tutte le domande.
Se hai programmato in C#, potresti essere così abituato ai tipi di riferimento che consentono valori nulli che potresti aver trascurato altre opportunità per dichiarare istanze non nullable.
- La raccolta di domande deve essere non annullabile.
- La raccolta di intervistati deve essere non annullabile.
Mentre scrivi il codice, noterai che un tipo di riferimento non annullabile come impostazione predefinita evita errori comuni che potrebbero portare a NullReferenceException. Una lezione di questa esercitazione è che sono state prese decisioni sulle variabili che potrebbero o non possono essere null
. Il linguaggio non ha fornito la sintassi per esprimere tali decisioni. Ora lo fa.
L'app che verrà compilata esegue i passaggi seguenti:
- Crea un sondaggio e aggiunge domande.
- Crea un set pseudo-casuale di intervistati per il sondaggio.
- Contatta gli intervistati fino a quando le dimensioni del sondaggio completate raggiungono il numero di obiettivo.
- Scrive statistiche importanti sulle risposte del sondaggio.
Costruire il sondaggio con tipi di riferimenti annullabili e non annullabili.
Il primo codice da scrivere crea il sondaggio. Scriverai classi per creare un modello di una domanda del sondaggio e una gestione del sondaggio. Il sondaggio ha tre tipi di domande, distinte in base al formato della risposta: Risposte sì/No, risposte numeri e risposte di testo. Creare una public SurveyQuestion
classe:
namespace NullableIntroduction
{
public class SurveyQuestion
{
}
}
Il compilatore interpreta ogni dichiarazione di variabile di tipo di riferimento come tipo di riferimento non annullabile per il codice in un contesto di annotazione annullabile abilitato. È possibile visualizzare il primo avviso aggiungendo proprietà per il testo della domanda e il tipo di domanda, come illustrato nel codice seguente:
namespace NullableIntroduction
{
public enum QuestionType
{
YesNo,
Number,
Text
}
public class SurveyQuestion
{
public string QuestionText { get; }
public QuestionType TypeOfQuestion { get; }
}
}
Poiché non hai inizializzato QuestionText
, il compilatore genera un avviso che una proprietà non annullabile non è stata inizializzata. La tua progettazione richiede che il testo della domanda sia non nullo, quindi aggiungi un costruttore per inizializzarlo insieme al valore QuestionType
. La definizione della classe completata è simile al codice seguente:
namespace NullableIntroduction;
public enum QuestionType
{
YesNo,
Number,
Text
}
public class SurveyQuestion
{
public string QuestionText { get; }
public QuestionType TypeOfQuestion { get; }
public SurveyQuestion(QuestionType typeOfQuestion, string text) =>
(TypeOfQuestion, QuestionText) = (typeOfQuestion, text);
}
L'aggiunta del costruttore rimuove l'avviso. L'argomento del costruttore è anche un tipo di riferimento non annullabile, pertanto il compilatore non genera avvisi.
Creare quindi una public
classe denominata SurveyRun
. Questa classe contiene un elenco di SurveyQuestion
oggetti e metodi per aggiungere domande al sondaggio, come illustrato nel codice seguente:
using System.Collections.Generic;
namespace NullableIntroduction
{
public class SurveyRun
{
private List<SurveyQuestion> surveyQuestions = new List<SurveyQuestion>();
public void AddQuestion(QuestionType type, string question) =>
AddQuestion(new SurveyQuestion(type, question));
public void AddQuestion(SurveyQuestion surveyQuestion) => surveyQuestions.Add(surveyQuestion);
}
}
Come in precedenza, è necessario inizializzare l'oggetto list in un valore non Null o il compilatore genera un avviso. Non sono presenti controlli di null nel secondo overload di AddQuestion
perché non sono necessari: la variabile è stata dichiarata non annullabile. Il valore non può essere null
.
Passare a Program.cs nell'editor e sostituire il contenuto di Main
con le righe di codice seguenti:
var surveyRun = new SurveyRun();
surveyRun.AddQuestion(QuestionType.YesNo, "Has your code ever thrown a NullReferenceException?");
surveyRun.AddQuestion(new SurveyQuestion(QuestionType.Number, "How many times (to the nearest 100) has that happened?"));
surveyRun.AddQuestion(QuestionType.Text, "What is your favorite color?");
Poiché l'intero progetto si trova in un contesto di annotazione annullabile abilitato, verranno generati avvisi quando si utilizza null
in qualsiasi metodo che si aspetta un tipo di riferimento non annullabile. Provare aggiungendo la riga seguente a Main
:
surveyRun.AddQuestion(QuestionType.Text, default);
Creare gli intervistati e ottenere risposte al sondaggio
Scrivere quindi il codice che genera risposte al sondaggio. Questo processo comporta diverse attività di piccole dimensioni:
- Costruire un metodo per generare oggetti di risposta. Questi rappresentano le persone che hanno chiesto di compilare il sondaggio.
- Creare la logica per simulare le domande a un risponditore e raccogliere risposte o notare che un rispondente non ha risposto.
- Ripetere fino a quando un numero sufficiente di intervistati non ha risposto al sondaggio.
Sarà necessaria una classe per rappresentare una risposta al sondaggio, quindi aggiungerla ora. Abilitare il supporto nullable. Aggiungere una Id
proprietà e un costruttore che lo inizializza, come illustrato nel codice seguente:
namespace NullableIntroduction
{
public class SurveyResponse
{
public int Id { get; }
public SurveyResponse(int id) => Id = id;
}
}
Aggiungere quindi un static
metodo per creare nuovi partecipanti generando un ID casuale:
private static readonly Random randomGenerator = new Random();
public static SurveyResponse GetRandomId() => new SurveyResponse(randomGenerator.Next());
La responsabilità principale di questa classe è generare le risposte per un partecipante alle domande nel sondaggio. Questa responsabilità prevede alcuni passaggi:
- Chiedere la partecipazione al sondaggio. Se la persona non acconsente, restituire una risposta mancante (o nulla).
- Porre ogni domanda e registrare la risposta. Ogni risposta può anche essere mancante (o null).
Aggiungere il codice seguente alla classe SurveyResponse
:
private Dictionary<int, string>? surveyResponses;
public bool AnswerSurvey(IEnumerable<SurveyQuestion> questions)
{
if (ConsentToSurvey())
{
surveyResponses = new Dictionary<int, string>();
int index = 0;
foreach (var question in questions)
{
var answer = GenerateAnswer(question);
if (answer != null)
{
surveyResponses.Add(index, answer);
}
index++;
}
}
return surveyResponses != null;
}
private bool ConsentToSurvey() => randomGenerator.Next(0, 2) == 1;
private string? GenerateAnswer(SurveyQuestion question)
{
switch (question.TypeOfQuestion)
{
case QuestionType.YesNo:
int n = randomGenerator.Next(-1, 2);
return (n == -1) ? default : (n == 0) ? "No" : "Yes";
case QuestionType.Number:
n = randomGenerator.Next(-30, 101);
return (n < 0) ? default : n.ToString();
case QuestionType.Text:
default:
switch (randomGenerator.Next(0, 5))
{
case 0:
return default;
case 1:
return "Red";
case 2:
return "Green";
case 3:
return "Blue";
}
return "Red. No, Green. Wait.. Blue... AAARGGGGGHHH!";
}
}
L'archiviazione per le risposte al sondaggio è rappresentata da un Dictionary<int, string>?
, il che indica che potrebbe essere null. Si sta usando la nuova funzionalità del linguaggio per dichiarare la finalità di progettazione, sia al compilatore che a chiunque legga il codice in un secondo momento. Se si esegue la dereferenziazione surveyResponses
senza prima verificare il null
valore, verrà visualizzato un avviso del compilatore. Non viene visualizzato un avviso nel AnswerSurvey
metodo perché il compilatore può determinare che la surveyResponses
variabile è stata impostata su un valore non Null precedente.
L'uso di null
per le risposte mancanti evidenzia un punto chiave per l'uso dei tipi di riferimento nullable: l'obiettivo non è rimuovere tutti i valori null
dal tuo programma. Invece, l'obiettivo è garantire che il codice scritto esprime la finalità della progettazione. I valori mancanti sono un concetto necessario per esprimere nel codice. Il null
valore è un modo chiaro per esprimere i valori mancanti. Il tentativo di rimuovere tutti i null
valori comporta solo la definizione di un altro modo per esprimere i valori mancanti senza null
.
Successivamente, è necessario scrivere il PerformSurvey
metodo nella SurveyRun
classe . Aggiungere il codice seguente nella SurveyRun
classe :
private List<SurveyResponse>? respondents;
public void PerformSurvey(int numberOfRespondents)
{
int respondentsConsenting = 0;
respondents = new List<SurveyResponse>();
while (respondentsConsenting < numberOfRespondents)
{
var respondent = SurveyResponse.GetRandomId();
if (respondent.AnswerSurvey(surveyQuestions))
respondentsConsenting++;
respondents.Add(respondent);
}
}
Anche in questo caso, la scelta di un valore nullable List<SurveyResponse>?
indica che la risposta può essere Null. Ciò indica che l'indagine non è stata ancora fornita ad alcun intervistato. Nota che gli intervistati vengono aggiunti fino a quando non si ottiene un numero adeguato di consensi.
L'ultimo passaggio per eseguire il sondaggio consiste nell'aggiungere una chiamata per eseguire il sondaggio alla fine del Main
metodo:
surveyRun.PerformSurvey(50);
Esaminare le risposte al sondaggio
L'ultimo passaggio consiste nel visualizzare i risultati del sondaggio. Aggiungerai codice a molte delle classi che hai scritto. Questo codice illustra il valore di distinguere i tipi di riferimento nullabile e non nullabile. Per iniziare, aggiungere i due membri con corpo di espressione seguenti alla SurveyResponse
classe :
public bool AnsweredSurvey => surveyResponses != null;
public string Answer(int index) => surveyResponses?.GetValueOrDefault(index) ?? "No answer";
Poiché surveyResponses
è un tipo riferimento nullable, i controlli Null sono necessari prima di de-referenziarlo. Il Answer
metodo restituisce una stringa non nullabile, quindi dobbiamo coprire il caso di una risposta mancante usando l'operatore null-coalescing.
Aggiungere quindi questi tre membri con corpo di espressione alla SurveyRun
classe :
public IEnumerable<SurveyResponse> AllParticipants => (respondents ?? Enumerable.Empty<SurveyResponse>());
public ICollection<SurveyQuestion> Questions => surveyQuestions;
public SurveyQuestion GetQuestion(int index) => surveyQuestions[index];
Il AllParticipants
membro deve tenere conto che la respondents
variabile potrebbe essere Null, ma il valore restituito non può essere Null. Se si modifica tale espressione rimuovendo ??
e la sequenza vuota che segue, il compilatore avvisa che il metodo potrebbe restituire null
e che la sua firma di ritorno prevede un tipo non annullabile.
Aggiungere infine il ciclo seguente nella parte inferiore del Main
metodo :
foreach (var participant in surveyRun.AllParticipants)
{
Console.WriteLine($"Participant: {participant.Id}:");
if (participant.AnsweredSurvey)
{
for (int i = 0; i < surveyRun.Questions.Count; i++)
{
var answer = participant.Answer(i);
Console.WriteLine($"\t{surveyRun.GetQuestion(i).QuestionText} : {answer}");
}
}
else
{
Console.WriteLine("\tNo responses");
}
}
Non sono necessari null
controlli in questo codice perché hai progettato le interfacce sottostanti in modo che tutti i tipi di riferimento restituiti siano non annullabili.
Ottenere il codice
È possibile ottenere il codice per l'esercitazione completata dal repository degli esempi nella cartella csharp/NullableIntroduction .
Prova a modificare le dichiarazioni di tipo tra tipi di riferimento annullabile e non annullabile. Guarda come genera avvisi diversi per evitare di dereferenziare accidentalmente un null
.
Passaggi successivi
Informazioni su come usare il tipo riferimento nullable quando si usa Entity Framework: