Après avoir lu cette question sur HNQ, j'ai continué à lire sur les types de référence Nullable en C # 8 et j'ai fait quelques expériences.
Je suis très conscient que 9 fois sur 10, voire plus souvent, quand quelqu'un dit "J'ai trouvé un bug de compilation!" c'est en fait par conception, et leur propre malentendu. Et depuis que j'ai commencé à étudier cette fonctionnalité seulement aujourd'hui, je ne l'ai clairement pas très bien comprise. Avec cela à l'écart, regardons ce code:
#nullable enable
class Program
{
static void Main()
{
var s = "";
var b = s == null; // If you comment this line out, the warning on the line below disappears
var i = s.Length; // warning CS8602: Dereference of a possibly null reference
}
}
Après avoir lu la documentation que j'ai liée à ci-dessus, je m'attendrais à ce que la s == null
ligne me donne un avertissement - après tout, il s
est clairement non nul, donc la comparer à null
n'a pas de sens.
Au lieu de cela, je reçois un avertissement sur la ligne suivante , et l'avertissement dit que s
c'est possible une référence nulle, même si, pour un humain, il est évident que ce n'est pas le cas.
De plus, l'avertissement ne s'affiche pas si nous ne comparons pas s
à null
.
J'ai fait quelques recherches sur Google et j'ai rencontré un problème avec GitHub , qui s'est avéré être quelque chose de complètement différent, mais au cours du processus, j'ai eu une conversation avec un contributeur qui a donné un aperçu de ce comportement (par exemple, "les vérifications nulles sont souvent un moyen utile de dire au compilateur de réinitialiser son inférence précédente sur la nullité d'une variable. " ). Cela m'a cependant laissé la question principale sans réponse.
Plutôt que de créer un nouveau problème GitHub, et de prendre potentiellement le temps des contributeurs du projet incroyablement occupés, je mets cela à la disposition de la communauté.
Pourriez-vous m'expliquer ce qui se passe et pourquoi? En particulier, pourquoi aucun avertissement n'est généré sur la s == null
ligne, et pourquoi avons-nous CS8602
quand il ne semble pas qu'une null
référence soit possible ici? Si l'inférence de nullité n'est pas à l'épreuve des balles, comme le suggère le thread GitHub lié, comment peut-il mal tourner? Quels seraient quelques exemples de cela?
la source
?
car ils
n'est pas nullable. Il ne devient pas annulable, simplement parce que nous étions assez stupides pour le comparernull
.Réponses:
C'est en fait un doublon de la réponse que @stuartd a liée, donc je ne vais pas entrer dans les détails super profonds ici. Mais la racine du problème est que ce n'est ni un bogue de langue ni un bogue de compilateur, mais c'est un comportement prévu exactement tel qu'implémenté. Nous suivons l'état nul d'une variable. Lorsque vous déclarez initialement la variable, cet état est NotNull car vous l'initialisez explicitement avec une valeur qui n'est pas nulle. Mais nous ne savons pas d'où vient ce NotNull. Ceci, par exemple, est en fait un code équivalent:
Dans les deux cas, vous testez explicitement
s
pournull
. Nous prenons cela comme entrée dans l'analyse de flux, tout comme Mads a répondu dans cette question: https://stackoverflow.com/a/59328672/2672518 . Dans cette réponse, le résultat est que vous obtenez un avertissement sur le retour. Dans ce cas, la réponse est que vous obtenez un avertissement que vous avez déréférencé une référence éventuellement nulle.Oui, en fait. Au compilateur. En tant qu'humains, nous pouvons regarder ce code et évidemment comprendre qu'il ne peut pas lever d'exception de référence nulle. Mais la façon dont l'analyse de flux nullable est implémentée dans le compilateur ne le peut pas. Nous avons discuté d'une certaine quantité d'améliorations à cette analyse où nous ajoutons des états supplémentaires en fonction de la provenance de la valeur, mais nous avons décidé que cela ajoutait beaucoup de complexité à la mise en œuvre pour pas beaucoup de gain, car les seuls endroits où cela serait utile dans des cas comme celui-ci, où l'utilisateur initialise une variable avec une
new
ou une valeur constante, puis la vérifienull
quand même.la source
s == null
ne produit-il pas d'avertissement?#nullable enable
;string s = "";s = null;
compile et fonctionne (il produit toujours un avertissement) quels sont les avantages de l'implémentation qui permet d'affecter null à une "référence non nullable" dans un contexte d'annotation null activé?s == null
. Par exemple, vous êtes peut-être dans une méthode publique et vous souhaitez effectuer la validation des paramètres. Ou, peut-être que vous utilisez une bibliothèque qui n'a pas été correctement annotée, et jusqu'à ce qu'ils corrigent ce bogue, vous devez gérer null là où il n'a pas été déclaré. Dans l'un ou l'autre de ces cas, si nous mettions en garde, ce serait une mauvaise expérience. Quant à permettre l'affectation: les annotations de variables locales sont juste pour la lecture. Ils n'affectent pas du tout l'exécution. En fait, nous mettons tous ces avertissements dans un seul code d'erreur afin que vous puissiez les désactiver si vous souhaitez réduire le taux de désabonnement du code.J'ai volontiers adopté les références annulables de C # 8 dès qu'elles étaient disponibles. Comme j'étais habitué à utiliser la notation [NotNull] (etc.) de ReSharper, j'ai remarqué quelques différences entre les deux.
Le compilateur C # peut être dupe, mais il a tendance à pécher par excès de prudence (généralement, pas toujours).
Comme référence pour les futurs visiteurs, voici les scénarios pour lesquels j'ai vu le compilateur être assez confus (je suppose que tous ces cas sont de par leur conception):
Il permet cependant d'utiliser une construction pour vérifier la nullité qui supprime également l'avertissement:
ou (encore assez cool) des attributs (trouvez-les tous ici ):
string?
n'est qu'une chaîne,int?
devient unNullable<int>
et oblige le compilateur à les gérer de manière sensiblement différente. Ici aussi, le compilateur choisit le chemin d'accès sécurisé, vous forçant à spécifier ce que vous attendez:Résolu donnant des contraintes:
Mais si nous n'utilisons pas de contraintes et supprimons le "?" à partir de Data, nous pouvons toujours y mettre des valeurs nulles en utilisant le mot-clé 'default':
Le dernier me semble le plus délicat, car il permet d'écrire du code dangereux.
J'espère que cela aide quelqu'un.
la source
ThrowIfNull(s);
m'assure qu'ils
n'est pas nul), n'existe pas. L'article explique également comment gérer les génériques non nullables , alors que je montrais comment vous pouvez "tromper" le compilateur, ayant une valeur nulle mais aucun avertissement à ce sujet.DoesNotReturnIf(bool)
.DoesNotReturnIfNull(nullable)
.