Dado o seguinte 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 que a instrução switch está ativada case var o
?
É meu entendimento que case string s
não corresponde a quando s == null
porque (efetivamente) (null as string) != null
avalia como falso. O IntelliSense no VS Code me diz que também o
é string
. Alguma ideia?
Semelhante a: caso de switch C # 7 com verificações nulas
o
éstring
(confirmado com genéricos - ou seja,Foo(o)
ondeFoo<T>(T template) => typeof(T).Name
) - é um caso muito interessante ondestring x
se comporta de forma diferente do quevar x
mesmo quandox
é digitado (pelo compilador) comostring
var
esse contexto. Isso com certeza parece o tipo de coisa que eu encontraria em C ++, não em uma linguagem que pretende levar o programador "ao poço do sucesso". Aqui,var
é ambíguo e inútil, coisas que o design C # normalmente parece se esforçar para evitar.switch
pode ser impronunciável - tipos anônimos, etc; e não é ambíguo - o compilador conhece claramente o tipo; é apenas confuso (pelo menos para mim) que asnull
regras sejam tão diferentes!Respostas:
Dentro de uma
switch
instrução de correspondência de padrões usando umcase
para um tipo explícito está perguntando se o valor em questão é daquele tipo específico ou um tipo derivado. É o equivalente exato deis
O valor
null
não tem um tipo e, portanto, não satisfaz nenhuma das condições acima. O tipo estático desomeString
não entra em jogo em nenhum dos exemplos.O
var
tipo embora na correspondência de padrões atua como um curinga e corresponderá a qualquer valor incluindonull
.O
default
caso aqui é um código morto. Ocase var o
corresponderá a qualquer valor, nulo ou não nulo. Um caso não padrão sempre vence o padrão, portantodefault
, nunca será atingido. Se você olhar para o IL, verá que nem mesmo é emitido.À primeira vista, pode parecer estranho que isso compile sem qualquer aviso (definitivamente me confundiu). Mas isso é compatível com o comportamento do C # que remonta a 1.0. O compilador permite
default
casos mesmo quando pode provar trivialmente que nunca será atingido. Considere como exemplo o seguinte:Aqui
default
nunca será atingido (mesmobool
que tenha um valor que não seja 1 ou 0). No entanto, o C # permite isso desde 1.0 sem aviso. A correspondência de padrões está apenas se alinhando com esse comportamento aqui.fonte
var
ser do tipostring
quando realmente não é (honestamente, não tenho certeza de qual tipo deve ser admitido)var
no exemplo é calculado para serstring
.csc /stiffUpperLip
null
é umastring
referência válida , e qualquerstring
referência (incluindonull
) pode ser convertida implicitamente (preservação de referência) para umaobject
referência e qualquerobject
referência quenull
pode ser convertida (explícita) com êxito para qualquer outro tipo, ainda sendonull
. Não é realmente a mesma coisa em termos de sistema de tipo de compilador.Estou juntando vários comentários do Twitter aqui - isso é realmente novo para mim, e espero que jaredpar me dê uma resposta mais abrangente, mas; versão curta como eu entendo:
é interpretado como
if(someString is string) { s = (string)someString; ...
ouif((s = (someString as string)) != null) { ... }
- qualquer um dos quais envolve umnull
teste - que falhou no seu caso; inversamente:onde o compilador resolve
o
comostring
está simplesmenteo = (string)someString; ...
- nenhumnull
teste, apesar do fato de que parece semelhante na superfície, apenas com o compilador fornecendo o tipo.finalmente:
aqui não pode ser alcançado , porque o caso acima abrange tudo. Isso pode ser um bug do compilador, pois ele não emitiu um aviso de código inacessível.
Concordo que isso é muito sutil, matizado e confuso. Mas aparentemente o
case var o
cenário tem usos com propagação nula (o?.Length ?? 0
etc). Concordo que é estranho que isso funcione de maneira muito diferente entrevar o
estring s
, mas é o que o compilador faz atualmente.fonte
É porque
case <Type>
corresponde ao tipo dinâmico (tempo de execução), não ao tipo estático (tempo de compilação).null
não tem um tipo dinâmico, por isso não pode ser comparadostring
.var
é apenas o retrocesso.(Postando porque gosto de respostas curtas.)
fonte