Étant donné le code suivant:
string someString = null;
switch (someString)
{
case string s:
Console.WriteLine("string s");
break;
case var o:
Console.WriteLine("var o");
break;
default:
Console.WriteLine("default");
break;
}
Pourquoi l'instruction switch correspond-elle sur case var o
?
Je crois comprendre que case string s
ne correspond pas lorsque s == null
parce que (effectivement) (null as string) != null
est évalué à faux. IntelliSense sur VS Code me dit que o
est aussi un string
. Des pensées?
Similaire à: C # 7 switch case with null checks
À l'intérieur d'un modèle correspondant à une instruction switch
utilisant un case
pour un type explicite, vous demandez si la valeur en question est de ce type spécifique ou d'un type dérivé. C'est l'équivalent exact de is
switch (someString) {
case string s:
}
if (someString is string)
La valeur null
n'a pas de type et ne remplit donc aucune des conditions ci-dessus. Le type statique de someString
n'entre en jeu dans aucun des deux exemples.
Le type var
bien qu'en correspondance de modèle agit comme un caractère générique et correspondra à toute valeur, y compris null
.
Le cas default
ici est du code mort. Le case var o
correspondra à n'importe quelle valeur, nulle ou non nulle. Un cas autre que par défaut l'emporte toujours sur un cas par défaut, donc default
ne sera jamais touché. Si vous regardez l'IL, vous verrez qu'il n'est même pas émis.
En un coup d'œil, il peut sembler étrange que cela compile sans aucun avertissement (m'a définitivement décontenancé). Mais cela correspond au comportement C # qui remonte à 1.0. Le compilateur autorise les cas default
même s'il peut prouver de manière triviale qu'il ne sera jamais touché. Prenons comme exemple ce qui suit:
bool b = ...;
switch (b) {
case true: ...
case false: ...
default: ...
}
Ici, default
ne sera jamais touché (même pour bool
dont la valeur n'est pas 1 ou 0). Pourtant, C # a permis cela depuis 1.0 sans avertissement. La correspondance de motifs est juste en ligne avec ce comportement ici.
Je rassemble plusieurs commentaires sur Twitter ici - c'est en fait nouveau pour moi, et j'espère que jaredpar interviendra avec une réponse plus complète, mais; version courte si je comprends bien:
case string s:
est interprété comme if(someString is string) { s = (string)someString; ...
ou if((s = (someString as string)) != null) { ... }
- l'un ou l'autre implique un test null
- qui a échoué dans votre cas; inversement:
case var o:
où le compilateur résout o
comme string
est simplement o = (string)someString; ...
- pas de null
test, malgré le fait qu'il ressemble en surface, juste avec le compilateur fournissant le type.
enfin:
default:
ici ne peut pas être atteint , car le cas ci-dessus attrape tout. Il peut s'agir d'un bogue du compilateur dans la mesure où il n'a pas émis d'avertissement de code inaccessible.
Je suis d'accord que c'est très subtil et nuancé, et déroutant. Mais apparemment, le scénario case var o
A des utilisations avec propagation nulle (o?.Length ?? 0
Etc.). Je suis d'accord qu'il est étrange que cela fonctionne si très différemment entre var o
Et string s
, Mais c'est ce que le le compilateur le fait actuellement.
C'est parce que case <Type>
correspond au type dynamique (au moment de l'exécution), pas au type statique (au moment de la compilation). null
n'a pas de type dynamique, il ne peut donc pas correspondre à string
. var
n'est que la solution de rechange.
(Publier parce que j'aime les réponses courtes.)