Explorando os Novos Recursos do C# 7.0

O C# 7.0 introduziu uma série de melhorias focadas em simplificar o consumo de dados, reduzir a verbosidade do código e otimizar o desempenho. Estas novidades, integradas ao Visual Studio 2017, trazem construções que tornam a lógica de negócios mais expressiva e eficiente.

Variáveis Out

Enteriormente, ao utilizar parâmetros out, era necessário declarar a variável antes de passá-la para o método, especificando seu tipo explicitamente. Com o C# 7.0, é possível declarar a variável diretamente no ponto de chamada.

public void ExibirDimensoes(Retangulo r)
{
    r.CalcularDimensoes(out int largura, out int altura);
    Console.WriteLine($"Largura: {largura}, Altura: {altura}");
}

A variável declarada fica disponível no escopo do bloco envolvente. Além disso, o compilador pode inferir o tipo, permitindo o uso de var:

r.CalcularDimensoes(out var largura, out var altura);

Isso é particularmente útil em padrões Try, onde o retorno booleano indica sucesso e o parâmetro out contém o resultado:

public void ProcessarIdade(string entrada)
{
    if (int.TryParse(entrada, out var idade))
    {
        Console.WriteLine($"Idade válida: {idade}");
    }
    else
    {
        Console.WriteLine("Entrada inválida.");
    }
}

Correspondência de Padrões (Pattern Matching)

A correspondência de padrões permite testar se um valor possui uma determinada "forma" e extrair informações dele quando há correspondência. O C# 7.0 aprimora as expressões is e as instruções switch.

Expressões Is com Padrões

Agora é possível usar padrões de tipo e constantes diretamente na expressão is:

public void AnalisarDados(object dado)
{
    if (dado is null) return;
    if (!(dado is decimal valor)) return;
    
    Console.WriteLine($"Valor processado: {valor}");
}

Instruções Switch Aprimoradas

O switch foi generalizado para aceitar qualquer tipo e utilizar padrões nas cláusulas case, além de permitir condições adicionais com a palavra-chave when.

switch(veiculo)
{
    case Carro c when c.Portas == 4:
        Console.WriteLine($"Sedan com {c.Portas} portas.");
        break;
    case Carro c:
        Console.WriteLine($"Carro comum com {c.Portas} portas.");
        break;
    case Moto m:
        Console.WriteLine($"Motocicleta cilindrada: {m.Cilindradas}cc");
        break;
    case null:
        throw new ArgumentNullException(nameof(veiculo));
    default:
        Console.WriteLine("Tipo de veículo desconhecido.");
        break;
}

A ordem das cláusulas case agora é estritamente avaliada de cima para baixo, semelhante aos blocos catch. O compilador alertará sobre casos inalcançáveis.

Tuplas

Retornar múltiplos valores de um método costumava ser verboso. O C# 7.0 introduz tipos e literais de tupla, eliminando a necessidade de criar classes temporárias ou usar out em excesso.

(string nome, int idade, bool ativo) ObterPerfilUsuario(string email)
{
    // Lógica de busca no banco de dados
    return ("João Silva", 30, true);
}

O chamador pode acessar os elementos pelos nomes personalizados:

var perfil = ObterPerfilUsuario("joao@email.com");
Console.WriteLine($"Nome: {perfil.nome}, Ativo: {perfil.ativo}");

Tuplas são tipos de valor (ValueTuple), o que evita alocações desnecessárias no heap, melhorando o desempenho em cenários de alta frequência.

Desconstrução

A desconstrução permite dividir uma tupla (ou qualquer objeto que implemente o método Deconstruct) em variáveis individuais.

var (nome, idade, ativo) = ObterPerfilUsuario("maria@email.com");
Console.WriteLine($"Usuário: {nome}");

Para classes personalizadas, basta implementar o método Deconstruct com parâmetros out:

public class Coordenada
{
    public double X { get; }
    public double Y { get; }

    public Coordenada(double x, double y) { X = x; Y = y; }
    
    public void Deconstruct(out double x, out double y) 
    { 
        x = X; 
        y = Y; 
    }
}

Isso permite desconstruir objetos diretamente:

var (posX, posY) = new Coordenada(10.5, 20.5);

Funções Locais

Funções locais permitem declarar métodos auxiliares dentro do escopo de outro método. Isso é ideal para encapsular lógica que não deve ser exposta ao restante da classe e para validar argumentos antes de executar iteradores.

public IEnumerable<int> GerarSequenciaPares(int maximo)
{
    if (maximo < 0) throw new ArgumentException("O valor deve ser positivo.");

    return Iterar();

    IEnumerable<int> Iterar()
    {
        for (int i = 0; i <= maximo; i++)
        {
            if (i % 2 == 0) yield return i;
        }
    }
}</int></int>

Neste exemplo, a validação do argumento ocorre imediatamente, enquanto a execução do iterador é adiada até que a coleção seja enumerada, mantendo o código limpo e seguro.

Tags: csharp-7 visual-studio-2017 pattern-matching tuples local-functions

Publicado em 7-14 02:14