Em uma versão recente do podcast DotNet & More Blazor, o NetCore 3.0 Preview, C # 8 e não apenas mencionamos casualmente um tópico tão ardente como o C # 8. A história sobre a experiência com o C # 8 não era grande o suficiente para lhe dedicar uma questão separada, por isso foi decidido compartilhar com ele os meios do gênero epistolar.
Neste artigo, gostaria de falar sobre minha experiência no uso do C # 8 na produção por 4 meses. Abaixo, você pode encontrar respostas para as seguintes perguntas:
- Como "soletrar" no novo c #
- Quais recursos foram realmente úteis
- O que decepcionou
Uma lista completa dos recursos do C # 8 pode ser encontrada na documentação oficial da Microsoft . Neste artigo, omitirei as oportunidades que não pude experimentar por um motivo ou outro, a saber:
- Membros somente leitura
- Membros da interface padrão
- Estruturas descartáveis ref
- Fluxos assíncronos
- Índices e intervalos
Proponho começar com uma das mais deliciosas possibilidades, como me pareceu antes.
Alternar expressões
Nos nossos sonhos, apresentamos essa função de maneira bastante otimista:
int Exec(Operation operation, int x, int y) => operation switch { Operation.Summ => x + y, Operation.Diff => x - y, Operation.Mult => x * y, Operation.Div => x / y, _ => throw new NotSupportedException() };
Mas, infelizmente, a realidade faz seus próprios ajustes.
Em primeiro lugar, não há possibilidade de combinar as condições:
string TrafficLights(Signal signal) { switch (signal) { case Signal.Red: case Signal.Yellow: return "stop"; case Signal.Green: return "go"; default: throw new NotSupportedException(); } }
Na prática, isso significa que, na metade dos casos, a expressão do comutador precisará ser transformada em um comutador regular para evitar copiar e colar.
Em segundo lugar, a nova sintaxe não suporta instruções, ou seja, código que não retorna um valor. Parece, bem, e não é necessário, mas fiquei surpreso quando percebi com que frequência o comutador é usado (em conjunto com a correspondência de padrões) para algo como afirmação em testes.
Em terceiro lugar, a expressão switch, que segue do último parágrafo, não suporta manipuladores de várias linhas. Quão assustador entendemos no momento de adicionar os logs:
int ExecFull(Operation operation, int x, int y) { switch (operation) { case Operation.Summ: logger.LogTrace("{x} + {y}", x, y); return x + y; case Operation.Diff: logger.LogTrace("{x} - {y}", x, y); return x - y; case Operation.Mult: logger.LogTrace("{x} * {y}", x, y); return x * y; case Operation.Div: logger.LogTrace("{x} / {y}", x, y); return x / y; default: throw new NotSupportedException(); } }
Eu não quero dizer que a nova opção é ruim. Não, ele é bom, apenas não é bom o suficiente.
Padrões de propriedade e posicionais
Há um ano, eles me pareciam os principais candidatos ao título de "oportunidade que mudou o desenvolvimento". E, como esperado, para usar todo o poder dos padrões posicionais e de propriedade, você precisa mudar sua abordagem ao desenvolvimento. Ou seja, é necessário imitar tipos de dados algébricos.
Parece, qual é o problema: pegue a interface do marcador e vá embora. Infelizmente, esse método tem uma séria desvantagem em um projeto grande: ninguém garante acompanhar em tempo de design a expansão de seus tipos algébricos. Portanto, é muito provável que, com o tempo, as alterações no código levem a muitas "falhas no padrão" nos locais mais inesperados.
Padrões de tupla
Mas o "irmão mais novo" das novas possibilidades de comparação com a amostra mostrou-se realmente bem-feito. O fato é que o padrão de tupla não requer nenhuma alteração na arquitetura familiar do nosso código, apenas simplifica alguns casos:
Player? Play(Gesture left, Gesture right) { switch (left, right) { case (Gesture.Rock, Gesture.Rock): case (Gesture.Paper, Gesture.Paper): case (Gesture.Scissors, Gesture.Scissors): return null; case (Gesture.Rock, Gesture.Scissors): case (Gesture.Scissors, Gesture.Paper): case (Gesture.Paper, Gesture.Rock): return Player.Left; case (Gesture.Paper, Gesture.Scissors): case (Gesture.Rock, Gesture.Paper): case (Gesture.Scissors, Gesture.Rock): return Player.Right; default: throw new NotSupportedException(); } }
Mas a melhor parte é que esse recurso, que é previsível o suficiente, funciona muito bem com o método Deconstruct. Basta passar uma classe com o Deconstruct implementado para alternar e usar os recursos do padrão de tupla.
Usando declarações
Parece uma oportunidade menor, mas traz muita alegria. Em todas as promoções, a Microsoft fala sobre esse aspecto como reduzir o aninhamento. Mas sejamos honestos, não tanto o que importa. Mas o que é realmente sério são os efeitos colaterais da exclusão de um bloco de código:
- Freqüentemente, ao adicionar usando, precisamos puxar o código "dentro" do bloco usando o método copiar e colar. Agora não pensamos nisso
- As variáveis declaradas dentro de using e usadas após Dispose do objeto using são uma verdadeira dor de cabeça. Um problema a menos
- Nas aulas que exigem chamadas Dispose frequentes, cada método teria duas linhas a mais. Parece um pouco, mas na condição de muitos métodos pequenos, esse pouco não permite exibir um número suficiente desses mesmos métodos em uma tela
Como resultado, o simples fato de usar declarações altera tanto a sensação de codificação que você simplesmente não deseja retornar ao c # 7.3.
Funções locais estáticas
Para ser sincero, se não fosse pela ajuda da análise de código, eu nem notaria essa possibilidade. No entanto, ela firmemente se estabeleceu em meu código: afinal, funções locais estáticas são perfeitamente adequadas para o papel de pequenas funções puras, pois não podem suportar o fechamento de variáveis de método. Como resultado, é mais fácil para o coração, porque você entende que há um erro potencial a menos no seu código.
Tipos de referência anuláveis
E para a sobremesa, gostaria de mencionar a característica mais importante do C # 8. Na verdade, a análise de tipos de referência anuláveis merece um artigo separado. Eu só quero descrever as sensações.
Sumário
Obviamente, as oportunidades apresentadas não alcançam uma revolução completa, mas há cada vez menos hiato entre C # e F # / Scala. Se é bom ou ruim, o tempo dirá.
No momento do lançamento deste artigo, o C # 8 já havia se estabelecido em seu projeto, então eu estaria me perguntando, quais são seus sentimentos sobre a nova versão do nosso idioma favorito?