Partager via



Février 2018

Volume 33, numéro 2

Cet article a fait l'objet d'une traduction automatique.

Essential .NET - C# 8.0 et types de référence Nullable

Par marque Michaelis | 2018 de février

Types référence Nullable, ce qui ? Ne sont pas tous les types référence nullable ? 

J’aime c# et de trouver la conception du langage prudent formidables. Néanmoins, tel quel actuellement, ainsi que les 7 versions de c#, nous n’avons toujours un langage parfait. Par que je veux dire que s’il est raisonnable d’attendre toujours certainement nouvelles fonctionnalités à ajouter à c#, il existe également, malheureusement, certains problèmes. Et, à des problèmes, je ne veux pas bogues mais, au lieu de cela, les problèmes de fondamentaux. Par exemple, un des zones à problèmes majeurs et un qui existe depuis c# 1.0 — entoure le fait que les types référence ne peuvent être null et, en fait, les types référence sont null par défaut. Voici quelques-unes des raisons pour lesquelles les types référence nullable sont idéales :

  • Émet une exception System.NullReferenceException appeler un membre d’une valeur null, et chaque appel entraîne un System.NullReferenceException dans le code de production est un bogue. Malheureusement, toutefois, avec les types référence nullable nous » se situent dans « accidentelle plutôt que de l’attitude de vos activités. L’action « se situent dans » est pour appeler un type référence sans vérifier les valeurs null.
  • Il existe une incohérence entre les types référence et les types de valeur (en suivant l’introduction de Nullable < T >) dans la mesure où les types valeur sont nullables lorsque décorée avec « ? » (par exemple, int ? nombre) ; dans le cas contraire, leur valeur par défaut à non nullable. En revanche, les types référence sont autorisant la valeur NULL par défaut. Il s’agit de « normal » à ceux qui ont été programmation en c# pour un certain temps, mais si nous pourrions le faire tout, que nous souhaitons serait la valeur par défaut pour les types de référence non Nullable et l’ajout d’un « ? » soit de façon explicite pour autoriser les valeurs NULL.
  • Il n’est pas possible d’exécuter l’analyse du flux statique pour vérifier tous les chemins d’accès concernant indique si une valeur sera null avant le déréférencement, ou non. Considérons, par exemple, s’il y a de code non managé, les appels multi-threading, ou null affectation/remplacement en fonction des conditions d’exécution. (Ne pas de mentionner si analysis inclut la vérification de la bibliothèque de toutes les API qui est appelées.)
  • Il n’existe aucune syntaxe raisonnable pour indiquer qu’une valeur de type référence null n’est pas valide pour une déclaration particulière.
  • Il n’existe aucun moyen pour décorer les paramètres pour ne pas autoriser la valeur null.

Comme déjà ladite, en dépit de cela, j’aime c# le point accepte uniquement le comportement de null comme une idiosyncrasy de c#. Avec c# 8.0, toutefois, l’équipe du langage c# est paramètre de déplacer les améliorer. Plus précisément, elle espère effectuer les opérations suivantes :

  • Fournir la syntaxe pour attendre la valeur null : Autorisez le développeur à identifier de manière explicite lorsqu’un type référence est censé contenir des valeurs NULL, et, par conséquent, pas les occasions indicateur lorsqu’il est explicitement affectée à null.
  • Vérifiez les types de référence par défaut attendent non nullable : Modifier l’attente de la valeur par défaut de tous les types de référence pour accepter la valeur null, mais le faire avec un commutateur de compilateur opt-in au lieu de soudainement surcharger le développeur avec des avertissements pour le code existant.
  • Diminuer l’occurrence des exceptions NullReferenceException : Réduire la probabilité d’exceptions de l’exception NullReferenceException en améliorant l’analyse du flux statique qui signale des occasions potentielles où une valeur n’a pas été vérifiée de façon explicite pour la valeur null avant d’appeler un des membres de la valeur.
  • Activer la suppression de l’avertissement d’analyse du flux statique : Prend en charge une forme de « confiance me, je suis un programmeur « déclaration qui permet au développeur de remplacer l’analyse du flux statique du compilateur et, par conséquent, de supprimer les avertissements d’une exception NullReferenceException possible.

Pour le reste de l’article, examinons chacun de ces objectifs et comment c# 8.0 implémente la prise en charge pour les fondamentaux dans le langage c#.

Fournit la syntaxe pour attendre la valeur Null

Pour commencer, il doit exister une syntaxe permettant de distinguer lorsqu’un type de référence doit attendre null et quand il ne devrait pas. À l’aide de la syntaxe évidente pour autorisant la valeur null est le ? comme une déclaration nullable, à la fois pour un type valeur et un type référence. En incluant la prise en charge sur les types référence, le développeur dispose d’un moyen d’opter pour la valeur null, par exemple :

string? text = null;

L’ajout de cette syntaxe explique pourquoi l’amélioration nullable critique est résumée par le nom apparemment à confusion, « types nullable référence ». Il n’est pas étant donné qu’un nouveau type de données référence acceptant les valeurs NULL, mais plutôt que présent, il est explicite, de participer : prise en charge pour le type de données de ladite.

Étant donné la syntaxe de types référence nullable, quelle est la syntaxe de type non nullable référence ?  Bien que ceci :

string! text = "Inigo Montoya"

peut sembler un bon choix, il présente la question que signifie simplement :

string text = GetText();

Examinons nous laissé avec trois déclarations, à savoir : types nullable de référence, les types référence non nullable et les types référence de-ne-savoir ? Euh, non !!

Au lieu de cela, nous souhaitons qu’est :

  • Types Nullable référence : chaîne ? texte = null ;
  • Types référence non-nullable : chaîne de texte = « Inigo Montoya »

Cela implique, bien entendu, un changement de langue importantes telles que des types de référence sans modificateur sont non nullable par défaut.

Vérifiez les Types de référence par défaut prévu Non Nullable

Changement des déclarations de référence standard (aucun modificateur autorisant la valeur null) pour accepter la valeur NULL est probablement la plus difficile de toutes les exigences pour réduire idiosyncrasy nullable. Le fait de la question est aujourd'hui, texte chaîne ; les résultats dans un type de référence appelé texte qui autorise null de texte, attend le texte à avoir la valeur null et, en fait, le texte de valeurs par défaut d’être null dans de nombreux cas, comme avec un champ ou d’un tableau. Toutefois, comme avec les types valeur, types de référence qui autorisent la valeur null doivent être l’exception, pas la valeur par défaut. Il serait préférable que lorsque nous null est assignée au texte ou Impossible d’initialiser le texte à un élément autre que null, le compilateur serait indicateur tout déréférencement de la variable de texte (le compilateur déjà indicateurs déréférencement d’une variable locale avant d’être initialisée).

Malheureusement, cela signifie que la modification de la langue et l’émission d’un avertissement lorsque vous affectez une valeur null (chaîne de texte = null, par exemple) ou d’affecter un type nullable référence (tels que chaîne ? texte = null ; chaîne moreText = texte ;). Le premier (chaîne de texte = null) est une modification avec rupture. (Émission d’un avertissement d’un élément qui n’induits aucun avertissement n’est une modification avec rupture.)  Pour éviter de surcharger les développeurs avec des avertissements dès qu’ils démarrent à l’aide du compilateur c# 8.0, à la place la prise en charge de la possibilité de valeur NULL est désactivée par défaut, par conséquent, aucune modification avec rupture. Pour tirer parti de celui-ci, par conséquent, vous devez participer en activant la fonctionnalité. (Toutefois, notez que dans la version d’évaluation disponible au moment de la rédaction, itl.tc/csnrtp, possibilité de valeur NULL est activée par défaut.)

Bien sûr, une fois que la fonctionnalité est activée, les avertissements apparaissent alors, en vous présentant le choix. Choisissez explicitement si le type de référence est conçu pour permettre les valeurs NULL, ou non. Si elle n’est pas le cas, supprimez l’affectation de null, supprimant ainsi l’avertissement. Toutefois, elle présente potentiellement un avertissement ultérieurement on, car la variable n’est pas affectée, et vous devez lui attribuer une valeur non null. Ou bien, si null est explicitement destiné (représentant « inconnu » par exemple), puis modifier le type de déclaration pour la valeur null, comme dans :

string? text = null;

Diminuer l’Occurrence de NullReferenceExceptions

Étant donné un moyen pour déclarer des types nullable ou non nullable, il est maintenant jusqu'à l’analyse du flux statique du compilateur pour déterminer quand la déclaration est potentiellement a violé. Lors de la déclaration d’un type référence comme nullable ou éviter une affectation de null à un type non nullable fonctionnera, nouveaux avertissements ou erreurs peuvent apparaître plus tard dans le code. En tant que référence déjà mentionné, acceptant les types provoque une erreur plus tard dans le code si la variable locale n’est jamais assignée (cela était vrai pour les variables locales avant c# 8.0). En revanche, l’analyse du flux statique est un indicateur déréférencer l’appel d’un type nullable pour laquelle il ne peut pas détecter une vérification préalable pour la valeur null et/ou toute assignation de la valeur nullable à une valeur autre que null. Figure 1 montre quelques exemples.

Figure 1 des exemples de résultats de l’analyse statique de flux

string text1 = null;
// Warning: Cannot convert null to non-nullable reference
string? text2 = null;
string text3 = text2;
// Warning: Possible null reference assignment
Console.WriteLine( text2.Length ); 
// Warning: Possible dereference of a null reference
if(text2 != null) { Console.WriteLine( text2.Length); }
// Allowed given check for null

Dans les deux cas, le résultat final est une diminution des exceptions NullReferenceException potentielles à l’aide d’analyse du flux statique pour vérifier une intention nullable.

Comme indiqué précédemment, l’analyse du flux statique doit signaler quand un type non nullable est potentiellement être affecté à null, soit affectée directement ou lorsqu’un type nullable. Malheureusement, cela n’est pas sans poser de problèmes. Par exemple, si une méthode déclare qu’il retourne un type de référence non nullable (par exemple une bibliothèque qui n’a pas encore été mis à jour avec des modificateurs de possibilité de valeur null) ou celle qui par erreur retourne null (par exemple un avertissement a été ignoré), ou une exception récupérable se produit et qu’un affectation attendue n’exécute pas, il est possible qu’un type référence non nullable pourriez finir avec une valeur null. Malheureusement, mais prise en charge pour les types référence nullable doivent diminuer la probabilité de lever une exception NullReferenceException, cependant pas supprimer. (Cela est analogue à la faillibilité d’une vérification du compilateur lorsque la variable est assignée.) De même, l’analyse du flux statique ne reconnaîtra pas toujours que le code, en fait, vérifie les valeurs null avant la référence à une valeur. En fait, l’analyse de flux uniquement vérifie la possibilité de valeur NULL dans un corps de méthode de variables locales et les paramètres et s’appuie sur les signatures de méthode et l’opérateur pour déterminer la validité. Il ne, par exemple, vous plonger dans le corps d’une méthode appelée IsNullOrEmpty pour exécuter une analyse sur indique si cette méthode correctement vérifie null telles qu’aucune vérification null supplémentaire n’est requise.

Activer la Suppression de l’avertissement d’analyse du flux statique

Étant donné la faillibilité possible de l’analyse du flux statique, que se passe-t-il si votre vérifier pour null (éventuellement avec un appel tel que de l’objet. ReferenceEquals (s, null) ou une chaîne. IsNullOrEmpty()) n’est pas reconnu par le compilateur ? Lorsque le programmeur sait mieux qu’une valeur ne va pas être null, ils peuvent le déréférencement suivant le ! opérateur (par exemple, texte !), comme dans :

string? text;...
if(object.ReferenceEquals(text, null))
{  var type = text!.GetType()
}

Sans le point d’exclamation, le compilateur vous avertit d’un appel de valeur null possible. De même, lorsque vous affectez une valeur nullable à une valeur non nullable vous pouvez la décorer la valeur assignée à un point d’exclamation pour informer le compilateur vous, le programmeur, savez :

string moreText = text!;

De cette façon, vous pouvez remplacer l’analyse du flux statique, tout comme vous pouvez utiliser un cast explicite. Bien entendu, lors de l’exécution la vérification appropriée se produiront.

Pour résumer

L’introduction du modificateur de possibilité de valeur NULL pour les types référence n’introduit un nouveau type. Les types référence sont toujours nullable et compilation chaîne ? résultats dans un langage intermédiaire qui est toujours System.String. La différence au niveau du langage intermédiaire est la décoration des types modifiés nullable dont l’attribut :

System.Runtime.CompilerServices.NullableAttribute

Par conséquent, les compile en aval peut continuer à tirer parti de l’intention déclarée. En outre, en supposant que l’attribut est disponible des versions antérieures de c# permettre toujours faire référence compilé en C# 8.0 bibliothèques, et ce sans les améliorations de la possibilité de valeur null. Plus important encore, cela signifie que les API existantes (par exemple, l’API .NET) peuvent être mis à jour avec les métadonnées nullable sans interruption de l’API. En outre, cela ne signifie qu’aucune prise en charge pour la surcharge basée sur le modificateur de possibilité de valeur null.

Il existe une conséquence regrettable null améliorer la gestion dans c# 8.0. La transition des déclarations traditionnellement nullables à acceptant introduira initialement un nombre important d’avertissements. Bien que cela soit regrettable, je pense qu’un équilibre raisonnable a été maintenu entre irritation et amélioration d’un est le code :

  • Avertissement vous permet de supprimer une attribution de null à un type non nullable potentiellement d’élimine un bogue, car une valeur n’est plus null quand il ne doit pas être.
  • Ou bien, ajout d’un modificateur autorisant des valeurs NULL améliore votre code en étant plus explicites à propos de votre intention.
  • Au fil du temps de qu'impédance entre nullable code mis à jour et de code plus ancien sera dissoudre, diminution de l’exception NullReferenceException bogues qui permet de se produire.
  • La fonctionnalité de possibilité de valeur NULL est désactivée par défaut sur les projets existants vous pouvez retarder le traitement avec lui jusqu'à une heure de votre choix. Au final, vous avez un code plus robuste. Pour les cas où vous connaissez mieux que le compilateur, vous pouvez utiliser le ! opérateur (déclaration, « confiance me, je suis un programmeur. ») comme un cast.

Autres améliorations dans c# 8.0

Il existe trois grands domaines supplémentaires d’amélioration envisagé pour c# 8.0 :

Flux de données asynchrone : Prise en charge des flux de données asynchrone permet await syntaxe d’itérer sur une collection de tâches (tâche < bool >). Par exemple, vous pouvez appeler

foreach await (var data in asyncStream)

et le thread ne bloquera pas pour des instructions suivant l’instruction await, mais à la place « continuera avec « les fois l’itération terminée. Et l’itérateur retournera l’élément suivant à la demande (la demande est un appel de la tâche < bool > MoveNextAsync sur l’itérateur du flux de données énumérable) suivi d’un appel à T en cours {get ;}.

Implémentations d’Interface par défaut : Avec c#, vous pouvez implémenter plusieurs interfaces telles que les signatures de chaque interface sont héritées. En outre, il est possible de fournir une implémentation d’un membre dans une classe de base afin que toutes les classes dérivées ont une implémentation par défaut du membre. Malheureusement, ce qui n’est pas possible doit implémenter plusieurs interfaces et fournissent également des implémentations par défaut de l’interface, autrement dit, l’héritage multiple. Avec l’introduction des implémentations d’interface par défaut, nous surmonter la restriction. En supposant qu'une implémentation par défaut raisonnable est possible, avec c# 8.0, vous serez en mesure d’inclure une implémentation d’un membre par défaut (propriétés et méthodes uniquement) et toutes les classes implémentant l’interface aura une implémentation par défaut. Alors que l’héritage multiple est un autre avantage, l’amélioration réelle ainsi est la possibilité d’étendre les interfaces avec des membres supplémentaires sans introduire une modification d’API avec rupture. Vous pouvez, par exemple, ajouter une méthode Count à IEnumerator < T > (bien que la mise en œuvre nécessiterait l’itération sur tous les éléments de la collection) sans rompre toutes les classes de l’interface implémentée. Notez que cette fonctionnalité requiert une version de framework correspondante (quelque chose qui n’a pas été requis depuis c# 2.0 et les génériques).

Extension de tous les éléments : Avec LINQ sont l’introduction de méthodes d’extension. Je n’oubliez pas avoir dîner avec Anders Hejlsberg au moment de la demande sur d’autres types d’extensions, telles que les propriétés. M. Hejlsberg m’a informé que l’équipe a été ne prendre en considération ce qui a été nécessaire pour l’implémentation de LINQ. À présent, 10 ans plus tard, hypothèse est réévaluée et ils envisagent l’ajout de méthodes d’extension pour les propriétés non seulement, mais également pour les événements, les opérateurs et même potentiellement constructeurs (le dernier s’ouvre un modèle de fabrique fascinants implémentations). L’un point important à retenir, en particulier lorsqu’il s’agit de propriétés, est que les méthodes d’extension sont implémentés dans les classes static et, par conséquent, il n’existe aucun état d’instance supplémentaires pour le type étendu introduit. Si vous demander de cet état, vous devez stocker dans une collection indexée par l’instance de type étendu, afin de récupérer l’état associé.


Mark Michaelisest le créateur de IntelliTect, où il sert de son architecte technique principal et le formateur. Pour près de vingt, il a été Microsoft MVP et directeur régional Microsoft depuis 2007. Michaelis fait plusieurs logiciels conception révision équipes Microsoft, notamment c#, Microsoft Azure, SharePoint et Visual Studio ALM. Il intervient lors de conférences et a écrit de nombreux ouvrages, y compris sa plus récente, « essentielles c# 7.0 (version 6) » (itl.tc/EssentialCSharp). Contactez-le sur Facebook à facebook.com/Mark.Michaelis, sur son blog à IntelliTect.com/Mark, sur Twitter : @markmichaelis ou par courrier électronique à Mark@IntelliTect.com.

Merci aux experts techniques Microsoft suivants d'avoir relu cet article : Kevin Bost, Grant Ericson, Tom œuvres, Mads Torgersen


Discussion sur cet article sur le forum MSDN Magazine