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 sno coincide cuando s == nullporque (efectivamente) se (null as string) != nullevalúa como falso. IntelliSense en VS Code me dice que oes unstring . ¿Alguna idea?
Similar a: caso de conmutador C # 7 con comprobaciones nulas

osestring(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 xse comporta de manera diferente quevar xincluso cuandoxse escribe (por el compilador) comostringvaren 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í,vares ambiguo e inútil, cosas que el diseño de C # normalmente parece esforzarse por evitar.switchpodría ser impronunciable - tipos anónimos, etc; y no es ambiguo : el compilador conoce claramente el tipo; ¡Es confuso (al menos para mí) que lasnullreglas sean tan diferentes!Respuestas:
Dentro de una
switchdeclaración de coincidencia de patrones que usacasepara 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 deisEl valor
nullno tiene un tipo y, por lo tanto, no cumple ninguna de las condiciones anteriores. El tipo estático desomeStringno entra en juego en ningún ejemplo.El
vartipo aunque en la coincidencia de patrones actúa como un comodín y coincidirá con cualquier valor incluyendonull.El
defaultcaso aquí es un código muerto. Elcase var ocoincidirá con cualquier valor, nulo o no nulo. Un caso no predeterminado siempre gana sobre uno predeterminado, pordefaultlo 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
defaultcasos incluso cuando puede demostrar trivialmente que nunca se alcanzará. Considere como ejemplo lo siguiente:Aquí
defaultnunca se activará (incluso sibooltiene 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
varque es del tipostringcuando en realidad no lo es (honestamente, no estoy seguro de cuál debería ser el tipo)varen el ejemplo esstring.csc /stiffUpperLipnulles unastringreferencia válida , y cualquierstringreferencia (incluidanull) se puede convertir implícitamente (preservando la referencia) a unaobjectreferencia, y cualquierobjectreferencia que senullpuede 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 unanullprueba) que ha fallado en su caso; Por el contrario:donde se resuelve el compilador
oquestringes simplementeo = (string)someString; ...- no haynullprueba, 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 oescenario tiene usos con propagación nula (o?.Length ?? 0etc.). Estoy de acuerdo en que es extraño que esto funcione de manera tan diferente entrevar oystring 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).nullno tiene un tipo dinámico, por lo que no puede coincidir constring.vares solo la alternativa.(Publicando porque me gustan las respuestas cortas).
fuente