Remarque
L’accès à cette page requiert une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page requiert une autorisation. Vous pouvez essayer de modifier des répertoires.
Tip
Nouveautés des types de référence nullables ? Lisez d’abord les types de référence Nullable . Ce tutoriel part du principe que vous comprenez la différence entre les types de référence non nullables et nullables et la façon dont le compilateur effectue le suivi de l’état null.
Venant d’une autre langue ? Si vous avez utilisé les types nullables de Kotlin, les options de TypeScript strictNullChecksou Swift, le modèle conceptuel est mappé directement. L’exercice ici concerne l’expression de l’intention de conception, et non l’apprentissage de la syntaxe.
Dans ce tutoriel, vous créez une petite bibliothèque qui modélise l’exécution d’une enquête. Les données ont deux modèles distincts que les types de référence nullables vous permettent de distinguer :
- Une question d’enquête doit toujours être présente. La liste des questions et le texte de chaque question ne peuvent jamais être
null. - Une réponse à une question peut être manquante. Les répondants peuvent refuser de répondre à certaines ou toutes les questions, et le modèle doit rendre ce modèle explicite.
Vous déclarez ces règles avec des types de référence non nullables et nullables. Le compilateur avertit ensuite chaque fois que le comportement du code ne correspond pas à la conception.
Dans ce tutoriel, vous allez :
- Créez l’application.
- Créez les questions d’enquête.
- Créez une enquête sur les questions.
- Testez la contrainte NOT NULL.
- Générer des types de réponse.
- Créez des répondants.
- Générez une réponse d’enquête.
- Créez un ensemble de réponses d’enquête.
- Examinez les résultats de l’enquête.
Trois classes modélisent l’enquête :
-
SurveyQuestion: une question. Le texte et le type de question sont requis. -
SurveyRun: collection de questions plus la liste des répondants. -
SurveyResponse: réponses d’un répondant, qui pourraient être manquantes.
Chaque type utilise des types référence non nullables pour les valeurs requises et les types de référence nullables pour les valeurs manquantes.
Prerequisites
- Le plus récent .NET SDK
- éditeur Visual Studio Code
- Le DevKit C#
Ce didacticiel suppose que vous êtes familiarisé avec C# et avec Visual Studio ou l’interface de ligne de commande .NET.
Créer l’application et activer des types de référence nullables
Créez une application console nommée NullableIntroduction:
dotnet new console -n NullableIntroduction
cd NullableIntroduction
Créer les questions relatives à l’enquête
Ajoutez un nouveau fichier nommé SurveyQuestion.cs au projet et remplacez son contenu par le code suivant. Le texte et le type de question ne sont pas nullables. Le constructeur doit donc initialiser les deux :
namespace NullableIntroduction;
public enum QuestionType
{
YesNo,
Number,
Text
}
public class SurveyQuestion(QuestionType typeOfQuestion, string text)
{
public string QuestionText { get; } = text;
public QuestionType TypeOfQuestion { get; } = typeOfQuestion;
}
Les paramètres du constructeur sont des types de référence non nullables. Par conséquent, le compilateur avertit l’appelant si l’un ou l’autre argument peut être null.
Créer une enquête sur les questions
Ensuite, ajoutez un nouveau fichier nommé SurveyRun.cs au projet et définissez une SurveyRun classe pour contenir la liste des questions :
namespace NullableIntroduction;
public class SurveyRun
{
private List<SurveyQuestion> surveyQuestions = [];
public void AddQuestion(QuestionType type, string question) =>
AddQuestion(new SurveyQuestion(type, question));
public void AddQuestion(SurveyQuestion surveyQuestion) =>
surveyQuestions.Add(surveyQuestion);
}
Le surveyQuestions champ est un champ non nullable List<SurveyQuestion>. Il utilise une expression de collection pour initialiser une liste vide. Les deux surcharges AddQuestion acceptent des paramètres non Nullable, donc le compilateur garantit que les appelants ne transmettent pas null.
Dans Program.cs, créez une SurveyRun et ajoutez trois questions :
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?");
Tester la contrainte non nulle
Pour voir comment le compilateur applique des paramètres non nullables, essayez d’ajouter la ligne suivante et de reconstruire :
surveyRun.AddQuestion(QuestionType.Text, default);
Le compilateur émet l’avertissement CS8625 parce que default a pour valeur null pour un type de référence, et AddQuestion attend un string non nullable. Supprimez la ligne avant de continuer.
Générer des types de réponse
Les répondants peuvent refuser de prendre l’enquête, et même lorsqu’ils participent, ils peuvent ignorer les questions individuelles. Les deux formes de résultats « manquants » sont des résultats valides, et le système de type doit les rendre visibles. Vous exprimez les deux formes avec null.
Ajoutez un nouveau fichier nommé SurveyResponse.cs au projet et définissez une SurveyResponse classe. Utilisez un constructeur principal (paramètres déclarés sur le type lui-même, disponibles dans tout le corps) pour capturer les éléments toujours requis Id:
namespace NullableIntroduction;
public class SurveyResponse(int id)
{
public int Id { get; } = id;
}
Créer des répondants
Ajoutez une méthode de fabrique statique (une static méthode qui crée et retourne une nouvelle instance du type, une alternative à l’appel du constructeur directement) qui crée des répondants avec un ID aléatoire :
private static readonly Random randomGenerator = new Random();
public static SurveyResponse GetRandomId() => new SurveyResponse(randomGenerator.Next());
Générer une réponse d’enquête
Ensuite, ajoutez la méthode qui demande l’enquête à un répondant. Stockez les réponses dans un dictionnaire nullable afin que le type lui-même communique que le répondant peut refuser :
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!";
}
}
Le surveyResponses champ est Dictionary<int, string>?. Si vous déréférencez le champ sans vérifier nulld’abord, le compilateur émet un avertissement. Dans AnswerSurvey, le compilateur sait que surveyResponses est non nul immédiatement après l’expression new, de sorte que le corps de la boucle ne nécessite aucune vérification supplémentaire.
Créer un ensemble de réponses d’enquête
Ajoutez à SurveyRun une méthode qui constitue une liste de répondants jusqu’à ce qu’un nombre suffisant d’entre eux consentent à participer :
private List<SurveyResponse>? respondents;
public void PerformSurvey(int numberOfRespondents)
{
int respondentsConsenting = 0;
respondents = [];
while (respondentsConsenting < numberOfRespondents)
{
var respondent = SurveyResponse.GetRandomId();
if (respondent.AnswerSurvey(surveyQuestions))
respondentsConsenting++;
respondents.Add(respondent);
}
}
Le champ respondents est List<SurveyResponse>? — il est null jusqu’à ce que l’enquête soit lancée.
Appelez PerformSurvey à partir de Main :
surveyRun.PerformSurvey(50);
Examiner les résultats de l’enquête
Pour signaler les résultats, exposez quelques fonctions utilitaires de SurveyResponse et SurveyRun. Dans SurveyResponse, ajoutez des membres à corps d’expression (membres définis avec => et une seule expression au lieu d’un bloc { ... }) qui gèrent le dictionnaire pouvant être null :
public bool AnsweredSurvey => surveyResponses != null;
public string Answer(int index) => surveyResponses?.GetValueOrDefault(index) ?? "No answer";
AnsweredSurvey vérifie le champ par rapport à null.
Answer utilise l’?.opérateur conditionnel de valeur Null (qui s’évalue à null lorsque le côté gauche est null au lieu de lever une exception) pour déréférer en toute sécurité, et l’??opérateur de coalescence Null (qui substitue l’opérande droit lorsque celui de gauche est null) pour fournir une valeur de remplacement non nulle. Le type de retour de la méthode n’est pas nullable string. Les appelants n’ont donc pas besoin de vérifications Null.
Dans SurveyRun, ajoutez des membres à corps d'expression exposant la liste des participants et des questions :
public IEnumerable<SurveyResponse> AllParticipants => (respondents ?? Enumerable.Empty<SurveyResponse>());
public ICollection<SurveyQuestion> Questions => surveyQuestions;
public SurveyQuestion GetQuestion(int index) => surveyQuestions[index];
AllParticipants retourne une séquence non nullable, même si respondents elle peut être null. L’opérateur ?? se substitue Enumerable.Empty<SurveyResponse>() lorsque le champ n’est pas encore rempli. Si vous supprimez la ?? clause, le compilateur avertit que la méthode peut retourner null malgré un type de retour non nullable.
Enfin, écrivez le rapport en bas de Main:
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");
}
}
Notez qu’aucune vérification null n’est nécessaire pour participant, surveyRun.Questionsou surveyRun.GetQuestion(i). Les types déclarent ces valeurs comme non nullables, de sorte que le compilateur les traite comme non null tout au long de la boucle.
Exécutez l’application :
dotnet run
La sortie est différente à chaque exécution, car les répondants sont générés de manière aléatoire, mais chaque ligne indique soit les réponses d’un participant, soit qu’il a refusé de répondre.
Conclusion
L’exemple terminé se trouve dans le dossier csharp/NullableIntroduction du référentiel dotnet/samples . Faites des essais en faisant passer les types de nullable à non nullable et inversement. La suppression d’un ? emplacement où la conception autorise les valeurs manquantes génère des avertissements du compilateur qui pointent vers chaque endroit où la valeur manquante est importante.