Dado el siguiente código:
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;
}
¿Por qué la declaración del interruptor coincide case var o
?
Tengo entendido que case string s
no coincide cuando s == null
porque (efectivamente) se (null as string) != null
evalúa como falso. IntelliSense en VS Code me dice que o
es unstring
. ¿Alguna idea?
Similar a: caso de conmutador C # 7 con comprobaciones nulas
o
sestring
(confirmado con los genéricos - es decirFoo(o)
, dondeFoo<T>(T template) => typeof(T).Name
) - se trata de un caso muy interesante en el questring x
se comporta de manera diferente quevar x
incluso cuandox
se escribe (por el compilador) comostring
var
en este contexto. Seguro que parece el tipo de cosas que encontraría en C ++, no en un lenguaje que pretende llevar al programador "al pozo del éxito". Aquí,var
es ambiguo e inútil, cosas que el diseño de C # normalmente parece esforzarse por evitar.switch
podría ser impronunciable - tipos anónimos, etc; y no es ambiguo : el compilador conoce claramente el tipo; ¡Es confuso (al menos para mí) que lasnull
reglas sean tan diferentes!Respuestas:
Dentro de una
switch
declaración de coincidencia de patrones que usacase
para un tipo explícito, se pregunta si el valor en cuestión es de ese tipo específico o de un tipo derivado. Es el equivalente exacto deis
El valor
null
no tiene un tipo y, por lo tanto, no cumple ninguna de las condiciones anteriores. El tipo estático desomeString
no entra en juego en ningún ejemplo.El
var
tipo aunque en la coincidencia de patrones actúa como un comodín y coincidirá con cualquier valor incluyendonull
.El
default
caso aquí es un código muerto. Elcase var o
coincidirá con cualquier valor, nulo o no nulo. Un caso no predeterminado siempre gana sobre uno predeterminado, pordefault
lo que nunca se alcanzará. Si miras el IL, verás que ni siquiera se emite.De un vistazo, puede parecer extraño que esto se compile sin ninguna advertencia (definitivamente me desconcertó). Pero esto coincide con el comportamiento de C # que se remonta a 1.0. El compilador admite
default
casos incluso cuando puede demostrar trivialmente que nunca se alcanzará. Considere como ejemplo lo siguiente:Aquí
default
nunca se activará (incluso sibool
tiene un valor que no sea 1 o 0). Sin embargo, C # ha permitido esto desde la versión 1.0 sin previo aviso. La coincidencia de patrones simplemente se ajusta a este comportamiento aquí.fuente
var
que es del tipostring
cuando en realidad no lo es (honestamente, no estoy seguro de cuál debería ser el tipo)var
en el ejemplo esstring
.csc /stiffUpperLip
null
es unastring
referencia válida , y cualquierstring
referencia (incluidanull
) se puede convertir implícitamente (preservando la referencia) a unaobject
referencia, y cualquierobject
referencia que senull
puede convertir con éxito (explícita) a cualquier otro tipo, aún siendonull
. No es realmente lo mismo en términos del sistema de tipos de compilador.Estoy reuniendo varios comentarios de Twitter aquí; esto es realmente nuevo para mí, y espero que Jaredpar intervenga con una respuesta más completa, pero; versión corta como yo lo entiendo:
se interpreta como
if(someString is string) { s = (string)someString; ...
oif((s = (someString as string)) != null) { ... }
(cualquiera de los cuales implica unanull
prueba) que ha fallado en su caso; Por el contrario:donde se resuelve el compilador
o
questring
es simplementeo = (string)someString; ...
- no haynull
prueba, a pesar del hecho de que sea similar en la superficie, sólo con el compilador proporcionar el tipo.finalmente:
aquí no se puede llegar , porque el caso anterior lo atrapa todo. Esto puede ser un error del compilador porque no emitió una advertencia de código inalcanzable.
Estoy de acuerdo en que esto es muy sutil, matizado y confuso. Pero aparentemente el
case var o
escenario tiene usos con propagación nula (o?.Length ?? 0
etc.). Estoy de acuerdo en que es extraño que esto funcione de manera tan diferente entrevar o
ystring s
, pero es lo que hace actualmente el compilador.fuente
Es porque
case <Type>
coincide con el tipo dinámico (tiempo de ejecución), no el tipo estático (tiempo de compilación).null
no tiene un tipo dinámico, por lo que no puede coincidir constring
.var
es solo la alternativa.(Publicando porque me gustan las respuestas cortas).
fuente