web-dev-qa-db-fra.com

commutateur avec un comportement étrange var / null

É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

90
budi

À 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.

69
JaredPar

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.

22
Marc Gravell

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.)

14
Mehrdad