Il existe un modèle dans les classes C # illustré par Dictionary.TryGetValue
et int.TryParse
: une méthode qui retourne un booléen indiquant le succès d'une opération et un paramètre out contenant le résultat réel; si l'opération échoue, le paramètre out est défini sur null.
Supposons que j'utilise des références C # 8 non nullables et que je souhaite écrire une méthode TryParse pour ma propre classe. La signature correcte est la suivante:
public static bool TryParse(string s, out MyClass? result);
Étant donné que le résultat est nul dans le faux cas, la variable out doit être marquée comme nullable.
Cependant, le modèle Try est généralement utilisé comme ceci:
if (MyClass.TryParse(s, out var result))
{
// use result here
}
Étant donné que je n'entre dans la branche que lorsque l'opération réussit, le résultat ne doit jamais être nul dans cette branche. Mais parce que je l'ai marqué comme nullable, je dois maintenant vérifier cela ou utiliser !
pour remplacer:
if (MyClass.TryParse(s, out var result))
{
Console.WriteLine("Look: {0}", result.SomeProperty); // compiler warning, could be null
Console.WriteLine("Look: {0}", result!.SomeProperty); // need override
}
C'est moche et un peu peu ergonomique.
En raison du modèle d'utilisation typique, j'ai une autre option: mentir sur le type de résultat:
public static bool TryParse(string s, out MyClass result) // not nullable
{
// Happy path sets result to non-null and returns true.
// Error path does this:
result = null!; // override compiler complaint
return false;
}
Maintenant, l'utilisation typique devient plus agréable:
if (MyClass.TryParse(s, out var result))
{
Console.WriteLine("Look: {0}", result.SomeProperty); // no warning
}
mais l'utilisation atypique ne reçoit pas l'avertissement qu'elle devrait:
else
{
Console.WriteLine("Fail: {0}", result.SomeProperty);
// Yes, result is in scope here. No, it will never be non-null.
// Yes, it will throw. No, the compiler won't warn about it.
}
Maintenant, je ne sais pas où aller ici. Y a-t-il une recommandation officielle de l'équipe de langage C #? Existe-t-il un code CoreFX déjà converti en références non nullables qui pourrait me montrer comment procéder? (Je suis allé chercher des TryParse
méthodes. IPAddress
Est une classe qui en a une, mais elle n'a pas été convertie sur la branche principale de corefx.)
Et comment le code générique Dictionary.TryGetValue
traite-t-il cela? (Peut-être avec un MaybeNull
attribut spécial de ce que j'ai trouvé.) Que se passe-t-il lorsque j'instancie un Dictionary
avec un type de valeur non nullable?
MyClass?
), et faites un interrupteur dessus avec uncase MyClass myObj:
et (un optionall)case null:
.Réponses:
Le modèle bool / out-var ne fonctionne pas bien avec les types de référence nullables, comme vous le décrivez. Donc plutôt que de combattre le compilateur, utilisez la fonctionnalité pour simplifier les choses. Ajoutez les fonctionnalités améliorées de correspondance de modèles de C # 8 et vous pouvez traiter une référence nullable comme un "type peut-être de pauvre":
De cette façon, vous évitez de jouer avec les
out
paramètres et vous n'avez pas à vous battre avec le compilateur pour le mélangenull
avec des références non nullables.À ce stade, ce «type peut-être du pauvre» tombe. Le défi auquel vous serez confronté est que lorsque vous utilisez des types de référence nullables (NRT), le compilateur sera traité
Foo<T>
comme non nullable. Mais essayez de le changerFoo<T?>
et il voudra queT
contraint à une classe ou à une structure en tant que types de valeur nullable soit une chose très différente du point de vue du CLR. Il existe une variété de solutions à ce problème:default
(avec!
) lesout
paramètres même si votre code ne s’inscrit à aucune valeur nulle,Maybe<T>
type que la valeur de retour, qui est alors jamaisnull
et enveloppe quibool
etout T
dansHasValue
etValue
propriétés ou quelque,Personnellement, je suis favorable à l'utilisation,
Maybe<T>
mais à ce qu'il prenne en charge une déconstruction afin qu'il puisse être associé à un motif comme un tuple comme en 4, ci-dessus.la source
TryParse(someString) is {} myClass
- Cette syntaxe prendra un certain temps pour s'y habituer, mais j'aime l'idée.TryParse(someString) is var myClass
me semble plus facile.x is var y
sera donc toujours vrai, qu'ilx
soit nul ou non.Si vous arrivez à cela un peu tard, comme moi, il s'avère que l'équipe .NET l'a abordé à travers un tas d'attributs de paramètres comme
MaybeNullWhen(returnValue: true)
dans l'System.Diagnostics.CodeAnalysis
espace que vous pouvez utiliser pour le modèle d'essai.Par exemple:
ce qui signifie que vous obtenez crié si vous ne vérifiez pas
true
Plus de détails:
la source
Je ne pense pas qu'il y ait de conflit ici.
votre objection à
est
Cependant, en fait, rien n'empêche l'affectation de null au paramètre out dans les anciennes fonctions TryParse.
par exemple.
L'avertissement donné au programmeur lorsqu'il utilise le paramètre out sans vérification est correct. Vous devriez vérifier!
Il y aura des tas de cas où vous serez obligé de retourner des types nullables où la branche principale du code retourne un type non nullable. L'avertissement est juste là pour vous aider à les rendre explicites. c'est à dire.
La manière non nullable de le coder lèvera une exception là où il y aurait eu un null. Que vous analysiez, obteniez ou commenciez
la source
FirstOrDefault
on puisse comparer, car la nullité de sa valeur de retour est le signal principal. Dans lesTryParse
méthodes, le paramètre out qui n'est pas nul si la valeur de retour est vraie fait partie du contrat de méthode.TryParse
méthode. SiIPAddress.TryParse
jamais retourné vrai mais n'assignait pas non nul à son paramètre out, je le signalerais comme un bug.