Padrões relacionais, lógicos e parênteses

Dica

Este artigo faz parte da seção Conceitos Básicos para desenvolvedores que já conhecem pelo menos uma linguagem de programação e estão aprendendo C#. Comece com a visão geral de correspondência de padrões se os padrões forem novos para você. Para obter regras de idioma completas, consulte padrões relacionais e padrões lógicos na referência de idioma.

Padrões relacionais e lógicos descrevem intervalos, alternativas e exclusões. O método a seguir os combina para classificar uma temperatura:

static string ClassifyTemperature(int temperature) =>
    temperature switch
    {
        < 0 => "Below freezing",
        >= 18 and <= 24 => "Comfortable",
        (>= 0 and < 10) or > 30 => "Far outside the comfortable range",
        _ => "Cool or warm"
    };

A expressão temperature é a entrada padrão. O C# o avalia uma vez, e cada ramo do switch testa o valor resultante. Os braços demonstram estes testes:

  • < 0 testa um limite.
  • >= 18 and <= 24 testa um intervalo.
  • (>= 0 and < 10) or > 30 testa dois intervalos alternativos.

Este artigo mostra padrões e condições imperativas para que você possa aprender cada formulário e comparar como eles expressam as mesmas decisões. Uma única condição pode parecer semelhante em qualquer uma das formas. Os padrões podem tornar uma série de ramificações relacionadas mais fácil de ler, mantendo as opções ao lado dos respectivos resultados. Escolha o formulário que torna o código mais fácil de entender.

Comparar valores com padrões relacionais

Um padrão relacional compara sua entrada padrão com uma constante de tempo de compilação usando <, >, ou >=<=. Uma constante de tempo de compilação é um valor que o compilador pode avaliar ao compilar o programa. Literais numéricos e de caracteres, e const variáveis de tipos numéricos ou de caracteres compatíveis, são exemplos representativos. Variáveis comuns, propriedades, chamadas de método e static readonly campos não são constantes de tempo de compilação. No exemplo inicial, tanto >= 18 quanto <= 24 testam o mesmo valor de temperature avaliado.

O mesmo símbolo relacional pode aparecer em uma expressão comum ou em um padrão. O exemplo a seguir usa ambas as formas com uma temperatura:

static void ShowExpressionAndPattern(int temperature, int threshold)
{
    bool belowThreshold = temperature < threshold;
    bool belowFreezing = temperature is < 0;

    string description = temperature switch
    {
        < 0 => "Freezing",
        0 => "Freezing point",
        > 0 => "Above freezing"
    };

    Console.WriteLine(
        $"Below threshold: {belowThreshold}; " +
        $"below freezing: {belowFreezing}; {description}");
}

temperature < threshold é uma expressão relacional. Ele avalia ambos os operandos e produz um bool. Qualquer operando pode ser uma expressão não constante.

In temperature is < 0, temperature é a expressão de entrada padrão. O C# o avalia e o padrão < 0 relacional testa o valor resultante. No braço do comutador < 0 => "Freezing", a expressão antes de switch fornece o valor de entrada, portanto o padrão contém apenas < 0.

A expressão pode ser comparada temperature com a variável threshold. Um operando de padrão relacional deve ser uma constante de tempo de compilação, portanto, use a expressão relacional quando o valor de comparação for uma variável. Quando o valor de comparação é constante, qualquer formulário pode funcionar.

Quando o operando direito for constante, priorize a legibilidade. Uma expressão relacional geralmente se encaixa em uma comparação direta. Um padrão relacional compõe-se com outros padrões e se ajusta naturalmente quando vários intervalos são mapeados para alternar resultados.

Combinar condições com padrões lógicos

Padrões lógicos combinam ou negam padrões com os operadores de padrão and, or e not:

  • Um and padrão corresponde quando ambos os padrões aninhados correspondem.
  • Um padrão or tem correspondência quando qualquer um dos dois padrões aninhados corresponde.
  • Um padrão not tem êxito quando seu padrão aninhado falha.

O exemplo de abertura usa and para descrever um intervalo e or descrever alternativas. Um not padrão pode excluir um valor, como em status is not Status.Complete. Os métodos a seguir mostram ambos os formulários para que você possa aprender sua sintaxe e comparar como eles expressam o mesmo teste:

static bool IsWeekendPattern(DayOfWeek day) =>
    day is DayOfWeek.Saturday or DayOfWeek.Sunday;

static bool IsWeekendImperative(DayOfWeek day) =>
    day == DayOfWeek.Saturday || day == DayOfWeek.Sunday;

O formulário padrão usa dois padrões constantes unidos por or. O formulário imperativo usa duas expressões de igualdade unidas pelo operador ||condicional OR. Ambos os formulários são concisos e claros para essa única condição. Escolha o formulário que melhor se ajusta ao código ao redor. Os padrões geralmente esclarecem várias opções relacionadas em um switch, como no exemplo de abertura.

Os operadores padrão formam padrões em vez de expressões boolianas: and corresponde à conjunção de padrão, or às alternativas de padrão e not à negação de padrão. Expressões boolianas usam &&, ||e !. Escolha or quando várias alternativas de padrão tiverem o mesmo resultado. Escolher not ao expressar o padrão excluído é mais claro do que listar todos os valores aceitos.

Padrões de grupo com parênteses

Um padrão parêntese usa parênteses para mostrar ou alterar como os padrões aninhados são agrupados. A associação determina quais operandos padrão um operador agrupa, semelhante ao agrupamento implícito quando você não escreve parênteses. O C# especifica a seguinte ordem de associação:

  1. not
  2. and
  3. or

O teste a seguir aceita as prioridades 1 a 3 ou a prioridade especial 9:

static bool IsAcceptedPriority(int priority) =>
    priority is (>= 1 and <= 3) or 9;

O compilador agrupa and antes de or. Os parênteses tornam o agrupamento pretendido explícito e as duas alternativas fáceis de ver: o intervalo de 1 a 3 ou 9. Para legibilidade, use parênteses sempre que um padrão se mistura and e orou quando not se aplica a um padrão composto. Parênteses também podem alterar o agrupamento padrão, como em not (>= 1 and <= 3).

A ordem de verificação em tempo de execução para padrões aninhados não é especificada, e os operadores de padrão seguem regras de correspondência de padrão em vez de regras booleanas de curto-circuito. Escreva padrões aninhados para que o resultado seja independente da ordem de verificação.

Use uma when cláusula de guarda para uma condição distinta

Os padrões lógicos funcionam melhor quando padrões aninhados descrevem o próprio valor de entrada. Um when guarda é uma condição booliana adicional em um case rótulo ou braço de comutador. Use um guarda quando a decisão também depender de informações separadas da entrada de padrão.

O aviso a seguir depende da temperatura e de um valor separado isOutdoors :

static string GetHeatWarning(int temperature, bool isOutdoors) =>
    temperature switch
    {
        > 35 when isOutdoors => "High heat outdoors",
        > 35 => "High heat",
        _ => "No heat warning"
    };

O padrão > 35 relacional descreve a temperature entrada. O guarda when isOutdoors verifica um valor separado. Um guarda também é preferível quando a condição precisa de uma chamada de método ou uma expressão booliana que a sintaxe padrão não expressa claramente.

Use padrões relacionais e lógicos quando eles tornarem os formatos ou valores permitidos da entrada mais fáceis de identificar, especialmente em várias ramificações do switch. Use uma expressão booliana comum quando indicar uma condição direta de forma mais simples. Use um when guarda quando uma opção depende de um valor separado ou de uma condição melhor expressa como uma expressão booliana.

Consulte também