Note
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier les répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de changer de répertoire.
Les types référence nullables complètent les types de référence de la même façon que les types valeur nullable complètent les types valeur. Vous déclarez une variable comme un type référence nullable en ajoutant un ? au type. Par exemple, string? représente une valeur Nullable string. Vous pouvez utiliser ces nouveaux types pour exprimer plus clairement votre intention de conception : certaines variables doivent toujours avoir une valeur , tandis que d’autres peuvent manquer une valeur.
Dans ce tutoriel, vous allez apprendre à :
- Incorporez des types de références annulables et non annulables dans vos conceptions
- Activez les vérifications de type de référence nullable dans l'ensemble de votre code.
- Écrivez du code dans lequel le compilateur applique ces décisions de conception.
- Utilisez la fonctionnalité de référence nullable dans la conception de vos propres projets
Conditions préalables
- La dernière version du SDK .NET
- Éditeur de code Visual Studio
- Le DevKit C#
Ce tutoriel suppose de connaître C# et .NET, y compris Visual Studio ou l’interface CLI .NET.
Incluez des types de référence nullables dans vos conceptions
Dans ce tutoriel, vous allez créer une bibliothèque qui modélise l’exécution d’une enquête. Le code utilise à la fois les types de référence nullables et les types de référence non nullables pour représenter les concepts réels. Les questions d’enquête ne peuvent jamais être nulles. Un répondant pourrait préférer ne pas répondre à une question. Les réponses peuvent être null dans ce cas.
Le code que vous écrivez pour cet exemple exprime cette intention et le compilateur applique cette intention.
Créer l’application et activer des types de référence nullables
Créez une nouvelle application console soit dans Visual Studio, soit en utilisant la ligne de commande avec dotnet new console. Nommez l’application NullableIntroduction. Une fois que vous avez créé l’application, vous devez spécifier que l’ensemble du projet se compile dans un contexte d’annotation nullable activé. Ouvrez le fichier .csproj et ajoutez un Nullable élément à l’élément PropertyGroup . Affectez-lui la valeur enable. Vous devez choisir la fonctionnalité des types de référence nullables dans les projets créés avant C# 11 / .NET 7. Une fois la fonctionnalité activée, les déclarations de variables de référence existantes deviennent des types de référence non nullables. Bien que cette décision vous aide à trouver des problèmes où le code existant n’a peut-être pas de vérifications null appropriées, il peut ne pas refléter avec précision votre intention de conception d’origine :
<Nullable>enable</Nullable>
Concevoir les types pour l’application
Cette application d’enquête nécessite la création de ces classes :
- Classe qui modélise la liste des questions.
- Classe qui modélise une liste de personnes contactées pour l’enquête.
- Classe qui modélise les réponses d’une personne qui a effectué l’enquête.
Ces types utilisent des types de référence nullables et non nullables pour exprimer les membres obligatoires et les membres facultatifs. Les types de référence nullables communiquent clairement l'intention de conception.
- Les questions qui font partie de l’enquête ne peuvent jamais être nulles : il n’est pas logique de poser une question vide.
- Les répondants ne peuvent jamais être nuls. Vous voulez suivre les personnes que vous avez contactés, même les répondants qui ont refusé de participer.
- Toute réponse à une question peut être null. Les répondants peuvent refuser de répondre à certaines ou toutes les questions.
Vous pourriez être tellement habitué aux types de référence qui autorisent des valeurs null qu'il se peut que vous manquiez d'autres opportunités de déclarer des instances non annulables :
- La collection de questions doit être non Nullable.
- La collection de personnes interrogées doit être non Nullable.
Lorsque vous écrivez le code, vous voyez qu’un type de référence non nullable comme valeur par défaut pour les références évite les erreurs courantes susceptibles de conduire à NullReferenceExceptions. Une leçon de ce tutoriel est que vous avez pris des décisions sur les variables qui pourraient ou ne pouvaient pas être null. Le langage n’a pas fourni de syntaxe pour exprimer ces décisions. Maintenant, c’est le cas.
L’application que vous générez effectue les étapes suivantes :
- Crée une enquête et y ajoute des questions.
- Crée un ensemble pseudo-aléatoire de répondants pour l’enquête.
- Contacte les répondants jusqu'à ce que la taille de l'enquête complète atteigne le nombre cible.
- Écrit des statistiques importantes sur les réponses de l’enquête.
Créer l’enquête avec des types référence nullable et non nullable
Le premier code que vous écrivez crée l’enquête. Vous écrivez des classes pour modéliser une question d’enquête et une exécution d’enquête. Votre sondage comporte trois types de questions, distingués par le format de la réponse : Oui/Non, réponses numériques et réponses textuelles. Créez une public SurveyQuestion classe :
namespace NullableIntroduction
{
public class SurveyQuestion
{
}
}
Le compilateur interprète chaque déclaration de variable de type référence comme type de référence non nullable pour le code dans un contexte d’annotation nullable activé. Vous pouvez voir votre premier avertissement en ajoutant des propriétés pour le texte de la question et le type de question, comme indiqué dans le code suivant :
namespace NullableIntroduction
{
public enum QuestionType
{
YesNo,
Number,
Text
}
public class SurveyQuestion
{
public string QuestionText { get; }
public QuestionType TypeOfQuestion { get; }
}
}
Comme vous n’avez pas initialisé QuestionText, le compilateur émet un avertissement indiquant qu’une propriété non nullable n’a pas été initialisée. Votre conception nécessite que le texte de la question soit non nul, donc vous ajoutez un constructeur pour l’initialiser ainsi que la valeur QuestionType. La définition de classe terminée ressemble au code suivant :
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’ajout du constructeur supprime la notification. L’argument du constructeur est également un type de référence non nullable. Le compilateur n’émet donc aucun avertissement.
Ensuite, créez une public classe nommée SurveyRun. Cette classe contient une liste d’objets et de SurveyQuestion méthodes pour ajouter des questions à l’enquête, comme indiqué dans le code suivant :
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);
}
}
Comme précédemment, vous devez initialiser l’objet de liste en une valeur non null ou le compilateur émet un avertissement. Il n’existe aucune vérification null dans la deuxième surcharge, AddQuestion car le compilateur permet d’appliquer le contrat non nullable : vous avez déclaré cette variable comme non nullable. Bien que le compilateur avertisse des potentielles affectations null, les valeurs null pendant l'exécution sont toujours possibles. Pour les API publiques, envisagez d’ajouter une validation d’argument même pour les types de référence non nullables, car le code client n’a peut-être pas activé de types de référence nullables ou peut passer intentionnellement null.
Basculez vers Program.cs dans votre éditeur et remplacez le contenu de Main par les lignes de code suivantes :
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?");
Étant donné que l’ensemble du projet se trouve dans un contexte d’annotation nullable activé, vous recevez des avertissements lorsque vous passez null à une méthode qui attend un type de référence non nullable. Essayez-le en ajoutant la ligne suivante à Main:
surveyRun.AddQuestion(QuestionType.Text, default);
Créer des répondants et obtenir des réponses à l’enquête
Ensuite, écrivez le code qui génère des réponses à l’enquête. Ce processus implique plusieurs petites tâches :
- Créez une méthode qui génère des objets répondants. Ces objets représentent les personnes invitées à remplir l’enquête.
- Créez une logique permettant de simuler la pose des questions à un répondant et de recueillir des réponses ou de noter qu’un répondant n’a pas répondu.
- Répétez jusqu’à ce que suffisamment de répondants répondent à l’enquête.
Vous avez besoin d’une classe pour représenter une réponse d’enquête. Ajoutez-le maintenant. Activez la prise en charge Nullable. Ajoutez une Id propriété et un constructeur qui l’initialise, comme indiqué dans le code suivant :
namespace NullableIntroduction
{
public class SurveyResponse
{
public int Id { get; }
public SurveyResponse(int id) => Id = id;
}
}
Ensuite, ajoutez une static méthode pour créer des participants en générant un ID aléatoire :
private static readonly Random randomGenerator = new Random();
public static SurveyResponse GetRandomId() => new SurveyResponse(randomGenerator.Next());
La principale responsabilité de cette classe est de générer les réponses d’un participant aux questions de l’enquête. Cette responsabilité comporte quelques étapes :
- Demandez la participation à l’enquête. Si la personne ne consent pas, retournez une réponse absente (ou nulle).
- Posez chaque question et enregistrez la réponse. Chaque réponse peut également être manquante (ou null).
Ajoutez le code suivant à la 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!";
}
}
Le stockage des réponses de l’enquête est un Dictionary<int, string>?, indiquant qu’il peut être nul. Vous utilisez la nouvelle fonctionnalité de langage pour déclarer votre intention de conception, à la fois au compilateur et à toute personne qui lit votre code ultérieurement. Si vous déréférencez surveyResponses sans d'abord vérifier la valeur de null, vous recevez un avertissement du compilateur. Vous n’obtenez pas d’avertissement dans la AnswerSurvey méthode, car le compilateur peut déterminer que la surveyResponses variable a été définie sur une valeur non null dans le code précédent.
L'utilisation de null pour les réponses manquantes met en évidence un point clé pour travailler avec des types de référence nullables : votre objectif n’est pas de supprimer toutes les valeurs null de votre programme. Au lieu de cela, votre objectif est de s’assurer que le code que vous écrivez exprime l’intention de votre conception. Les valeurs manquantes sont un concept nécessaire pour exprimer votre code. La null valeur est un moyen clair d’exprimer ces valeurs manquantes. La tentative de suppression de toutes les null valeurs entraîne la définition d’une autre façon d’exprimer ces valeurs manquantes sans null.
Ensuite, vous devez écrire la PerformSurvey méthode dans la SurveyRun classe. Ajoutez le code suivant dans la 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);
}
}
Ici encore, votre choix d’une valeur Nullable List<SurveyResponse>? indique que la réponse peut être null. Cela indique que l’enquête n’a pas encore été donnée aux répondants. Notez que les répondants sont ajoutés jusqu’à ce que le consentement soit suffisant.
La dernière étape pour exécuter l’enquête consiste à ajouter un appel pour effectuer l’enquête à la fin de la Main méthode :
surveyRun.PerformSurvey(50);
Examiner les réponses d’enquête
La dernière étape consiste à afficher les résultats de l’enquête. Vous ajoutez du code à de nombreuses classes que vous avez écrites. Ce code illustre l'importance de distinguer les types de référence nullables et non-nullables. Commencez par ajouter les deux membres à corps d'expression suivants à la classe SurveyResponse :
public bool AnsweredSurvey => surveyResponses != null;
public string Answer(int index) => surveyResponses?.GetValueOrDefault(index) ?? "No answer";
Étant donné qu’il surveyResponses s’agit d’un type de référence nullable, des vérifications de nullité sont nécessaires avant de le déréférencer. La Answer méthode retourne une chaîne non nullable. Nous devons donc couvrir le cas d’une réponse manquante à l’aide de l’opérateur de fusion null.
Ensuite, ajoutez ces trois membres expression-bodied à la classe SurveyRun :
public IEnumerable<SurveyResponse> AllParticipants => (respondents ?? Enumerable.Empty<SurveyResponse>());
public ICollection<SurveyQuestion> Questions => surveyQuestions;
public SurveyQuestion GetQuestion(int index) => surveyQuestions[index];
Le AllParticipants membre doit tenir compte du fait que la respondents variable peut être null, mais que la valeur de retour ne peut pas être null. Si vous modifiez cette expression en supprimant le ?? et la séquence vide qui suit, le compilateur vous avertit que la méthode pourrait retourner null et sa signature de retour retourne un type non-nullable.
Enfin, ajoutez la boucle suivante en bas de la Main méthode :
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");
}
}
Vous n’avez pas besoin null de vérifications dans ce code, car vous avez conçu les interfaces sous-jacentes pour qu’elles retournent des types de référence non nullables. L’analyse statique du compilateur permet de s’assurer que ces contrats de conception sont suivis.
Obtenir le code
Vous pouvez obtenir le code du didacticiel terminé à partir de notre référentiel d’exemples dans le dossier csharp/NullableIntroduction .
Expérimentez en modifiant les déclarations de type entre les types de référence nullables et non nullables. Découvrez comment cela génère différents avertissements pour vous assurer que vous ne déréférencez accidentellement pas un null.
Étapes suivantes
Découvrez comment utiliser le type de référence Nullable lors de l’utilisation d’Entity Framework :