Declaração, constante e padrões var

Tip

Este artigo faz parte da secção Fundamentos para programadores que já conhecem pelo menos uma linguagem de programação e estão a aprender C#. Começa com a visão geral da correspondência de padrões se nunca usaste padrões C# antes. Para regras completas de linguagem, consulte a referência de padrões.

Um padrão é aplicado a uma expressão de entrada. C# avalia a expressão, depois o padrão testa ou captura o valor resultante. Os padrões de declaração, de constante e var respondem a três perguntas práticas:

  • Padrão de declaração: A expressão produziu um valor não nulo de um tipo de execução compatível? Se sim, declare uma variável para esse valor.
  • Padrão constante: A expressão produziu um valor constante específico?
  • var Padrão: Que valor teve a expressão? Captura-o sem antes testar o seu tipo ou valor.

Testar e capturar um tipo com um padrão de declaração

Um padrão de declaração consiste num tipo e numa designação. O tipo especifica que tipo de execução testar. A designação declara a variável que recebe o valor correspondente.

O exemplo seguinte recebe um object, pelo que a expressão pode produzir muitos tipos diferentes. O padrão de declaração permite que o ramo de correspondência utilize um valor decimal sem um teste de tipo separado e uma conversão:

static void PrintPrice(object value)
{
    if (value is decimal amount)
    {
        Console.WriteLine($"Price: {amount:C}");
    }
}

Em value is decimal amount:

  • value é a expressão de entrada. C# avalia-o primeiro.
  • decimal é o tipo testado. O padrão corresponde quando o valor avaliado não é nulo e o seu tipo de tempo de execução é compatível com decimal.
  • amount é a designação. Quando o padrão coincide, declara amount e atribui-lhe o valor decimal.

O compilador acompanha se uma variável local recebe um valor antes do seu código o ler. Este acompanhamento chama-se atribuição definida. Dentro do bloco if, o compilador sabe que foi atribuído um valor a amount, porque o bloco só é executado quando o padrão corresponde. O compilador produz um erro se o seu código tentar aceder amount fora do if bloco. Se value não for um decimal valor, a variável amount não é atribuída a um valor.

Escolha um padrão de declaração quando o ramo correspondente precisar de usar o resultado como tipo testado. Combina o teste, a conversão e a declaração da variável, evitando assim repetir a expressão ou escrever um cast separado.

Também pode usar padrões de declaração quando uma expressão pode produzir vários tipos úteis:

static string FormatSensorValue(object reading) =>
    reading switch
    {
        int count => $"Count: {count}",
        double temperature => $"Temperature: {temperature:F1}°C",
        string message => $"Message: {message}",
        _ => "Unsupported reading"
    };

Cada braço declara uma variável do tipo correspondente porque o resultado precisa do comportamento de formatação desse tipo. Um padrão de declaração corresponde apenas quando o valor avaliado não é nulo e já tem um tipo de execução compatível com o tipo testado através das conversões permitidas para padrões. null não tem um tipo de tempo de execução para o padrão corresponder. O padrão também não executa operadores de conversão definidos pelo utilizador: é um teste e captura de tipos, não um pedido para converter o valor para outro tipo. Para as regras completas de compatibilidade, veja Padrões de Declaração e de tipos.

Corresponder a um valor específico com um padrão constante

Um padrão constante testa se uma expressão produz uma constante particular, como um número, cadeia, booleano, membro enum, valor declarado const , ou null.

Padrões constantes ajustam-se a uma expressão switch quando vários valores conhecidos produzem um resultado diferente:

static string GetCommandMessage(Command command) =>
    command switch
    {
        Command.Start => "Starting",
        Command.Stop => "Stopping",
        Command.Pause => "Pausing",
        _ => "Unknown command"
    };

Command é um enum, um tipo que define um conjunto de constantes nomeadas. Command.Start, Command.Stop, e Command.Pause são os seus membros enum, pelo que cada braço do switch usa um padrão constante para testar um valor de comando nomeado.

Escolha esta forma quando o comando pode ter vários significados distintos. Os braços do interruptor mantêm os valores e os seus resultados juntos. Para uma simples comparação de igualdade, uma if afirmação como if (command == Command.Start) é geralmente mais fácil de ler.

A correspondência de padrões constantes utiliza regras de igualdade de linguagem incorporadas em vez de um operador definido == pelo utilizador. Para as regras detalhadas de igualdade e conversão, veja a referência do padrão constante.

O null padrão constante é útil para uma verificação fiável do nulo:

static bool HasText(string? text) => text is not null;

Escolhe is null ou is not null quando estiveres a verificar o estado nulo. Estes padrões não chamam um operador de igualdade definido pelo utilizador, mesmo que o tipo da expressão sobrecarregue ==.

Captura um resultado para uma guarda com um var padrão

Um var padrão corresponde a todos os resultados, incluindo null, e declara uma variável cujo tipo é o tipo da expressão de entrada em tempo de compilação. Pode capturar um valor calculado enquanto outro padrão já está a corresponder a um objeto:

static string GetDeliveryMessage(object delivery) =>
    delivery switch
    {
        ExpressDelivery express
            when EstimateDays(express) is var days && days <= 2
                => $"Arrives in {days} day{(days == 1 ? "" : "s")}",
        ExpressDelivery => "Express delivery for your location takes more than two days",
        _ => "Standard delivery"
    };

static int EstimateDays(ExpressDelivery delivery) =>
    delivery.MilesAway <= 500 ? 1 :
    delivery.MilesAway <= 1_000 ? 2 : 3;

record ExpressDelivery(int MilesAway);

O padrão ExpressDelivery express de declaração captura primeiro o objeto de entrega como express. A chamada ao método EstimateDays(express) é a expressão de entrada do padrão var. C# avalia essa chamada ao método e var days captura a estimativa resultante como days, sem testar o respetivo tipo nem valor. A estimativa pode ser de um ou dois dias quando a guarda tem sucesso. O resultado do arm precisa do valor capturado para indicar o número real de dias.

Uma variável local comum não pode ser declarada entre um padrão de ramo de switch e a sua when condição de guarda. Chamar EstimateDays(express) novamente no resultado repetiria o cálculo. Escolha esta var forma de padrão quando o código já estiver a corresponder, e tanto a guarda como o resultado precisam de um valor intermédio calculado.

Se não precisares do valor capturado, usa o padrão _de descarte em vez de declarar uma variável.

Consulte também