Por que esse código funciona? Estou usando o C # 8 com o Visual Studio 2019.
Você respondeu sua própria pergunta! É porque você está usando C # 8.
A regra de C # 1 a 7 era: um nome simples não pode ser usado para significar duas coisas diferentes no mesmo escopo local. (A regra real era um pouco mais complexa que essa, mas descreve como é tedioso; consulte a especificação do C # para obter detalhes.)
A intenção dessa regra era impedir o tipo de situação que você está falando no seu exemplo, onde fica muito fácil confundir o significado do local. Em particular, essa regra foi projetada para evitar confusões como:
class C
{
int x;
void M()
{
x = 123;
if (whatever)
{
int x = 356;
...
E agora temos uma situação em que, dentro do corpo de M
, x
significa ambos this.x
e o local x
.
Embora bem-intencionado, houve vários problemas com essa regra:
- Não foi implementado para especificação. Havia situações em que um nome simples podia ser usado como, digamos, um tipo e uma propriedade, mas nem sempre eram sinalizados como erros porque a lógica de detecção de erros era falha. (Ver abaixo)
- As mensagens de erro foram redigidas de maneira confusa e relatadas de maneira inconsistente. Havia várias mensagens de erro diferentes para essa situação. Eles identificaram inconsistentemente o agressor; isto é, às vezes o uso interno seria destacado, às vezes o externo , e às vezes era apenas confuso.
Fiz um esforço na reescrita de Roslyn para resolver isso; Eu adicionei algumas novas mensagens de erro e tornei as antigas consistentes em relação a onde o erro foi relatado. No entanto, esse esforço foi muito pouco, muito tarde.
A equipe do C # decidiu para o C # 8 que toda a regra estava causando mais confusão do que impedia, e a regra foi retirada do idioma. (Obrigado Jonathon Chase por determinar quando a aposentadoria aconteceu.)
Se você estiver interessado em aprender o histórico desse problema e como tentei corrigi-lo, consulte estes artigos que escrevi sobre ele:
https://ericlippert.com/2009/11/02/simple-names-are-not-so-simple/
https://ericlippert.com/2009/11/05/simple-names-are-not-so-simple-part-two/
https://ericlippert.com/2014/09/25/confusing-errors-for-a-confusing-feature-part-one/
https://ericlippert.com/2014/09/29/confusing-errors-for-a-confusing-feature-part-two/
https://ericlippert.com/2014/10/03/confusing-errors-for-a-confusing-feature-part-three/
No final da parte três, observei que havia também uma interação entre esse recurso e o recurso "Color Color" - ou seja, o recurso que permite:
class C
{
Color Color { get; set; }
void M()
{
Color = Color.Red;
}
}
Aqui usamos o nome simples Color
para se referir a ambos this.Color
e ao tipo enumerado Color
; de acordo com uma leitura estrita da especificação, isso deve ser um erro, mas, neste caso, a especificação estava errada e a intenção era permiti-la, pois esse código é inequívoco e seria irritante fazer o desenvolvedor alterá-lo.
Eu nunca escrevi esse artigo descrevendo todas as interações estranhas entre essas duas regras, e seria um pouco inútil fazê-lo agora!
x
parâmetro desse método é movido para fora do escopo. Veja sharplab para um exemplo.