Pattern Matching é uma técnica para testar uma expressão para determinar se ela tem determinadas características. [Microsoft C# Pattern Matching]
Pattern Matching faz o código ser mais fácil de ler e entender. Veja os exemplos abaixo.
// Sem Pattern Matching
if (video.Provider.GetType() == typeof(VimeoProvider))
{
var videoProvider = (VimeoProvider)order;
if (videoProvider.ContaAtiva) // Propriedade genérica que VimeoProvider herdou.
{
if (videoProvider.ContaPremium) // Propriedade exclusiva no tipo VimeoProvider e não disponivel em outros tipos.
{ }
}
}
Mas com Pattern Matching fica dessa forma para fazer a mesma coisa que o código acima.
// Com Pattern Matching
if (video.Provider is VimeoProvider { ContaAtiva: true } videoProvider)
{
if (videoProvider.ContaPremium)
{ }
}
No exemplo acima são utilizados vários padrões onde operador is
em video.Provider is VimeoProvider é usado para verificar se os tipos são iguais (Type Pattern), e caso seja igual então é acessado o valor da propriedade { ContaAtiva: true } usando Property Pattern para fazer a comparação lógica do valor, e por ultimo, caso tenha sucesso, é usado Declaration Pattern para criar uma variável local com o nome videoProvider do tipo VimeoProvider.
Mas calma porque vou apresentar os tipos para você entender o código acima.
Todos os padrões podem ser utilizados de três formas:
- is operator
- switch statement
- switch expression
Os exemplos mostrados em cada explicação serão alternados entre as três formas para mostrar como funciona.
Em .NET, existem vários padrões, segue a lista:
- Constant Pattern
- Discard Pattern
- Var Pattern
- Type Pattern
- Declaration Pattern
- Relational Patterns
- Logical Patterns
- Property Patterns
- Positional Pattern
- Parenthesized Pattern
- Referência
Constant Pattern
Verifica se um valor é igual a um valor constante especificado. Pode ser utilizado com os tipos numéricos, char, string, boolean, null e enums.
var resultado = 4 switch
{
1 => "Um",
2 => "Dois",
3 => "Trê",
4 => "Quatro",
5 => "Cinco",
};
// resultado: Quatro
Será lançada uma exceção caso informe um valor diferente do que foi definido nas condições do switch expression. Para resolver esse problema será utilizado o Discart Pattern.
Discard Pattern
Trate de forma genérica qualquer valor que não foi explicitamente definido na condição.
É representado pelo símbolo _
no lugar do nome de uma variável e ignora o valor atribuído a ela.
Em condições com switch
o _
descarta qualquer outro valor não definido nas condições. É parecido com o default case
do switch
e por ser mais genérico que outras condições é definido na última expressão a ser analisada e caso seja definida em qualquer outra posição acontecerá um erro de compilação.
Quando utilizado no lugar de nomes de variáveis o valor é descartado, da mesma forma que é utilizado em descontructor
de tuplas.
var tupla = (1, 4, 7);
if (tupla is (1, _, 7)) // segundo valor é ignorado
{ }
var resultado = 9 switch
{
1 => "Um",
2 => "Dois",
3 => "Três",
4 => "Quatro",
5 => "Cinco",
_ => "Outro valor"
};
// resultado: Outro valor
Var Pattern
Um valor, depois de testado e ter tido sucesso, é atribuído a uma variável local e pode ser utilizado depois de =>
para criar um valor de retorno.
var tupla = (2, 1);
var resultado = tupla switch
{
(_, var y) when y > 4 => $"Y: {y} é maior que 4",
var (x, y) when x < y => $"{x} menor que {y}",
(var x, var y) when x > y => $"{x} maior que {y}",
_ => "Outro valor"
};
Type Pattern
Verifica se um tipo é igual ao outro.
Antes do Type Pattern era necessário verificar os tipos usando GetType()
ou typeof
como no exemplo abaixo.
if (videoProvider.GetType() == typeof(YoutubeProvider))
{ }
A mesma verificação com Type Pattern ficaria assim.
if (videoProvider is YoutubeProvider)
{ }
Exemplo com switch expression
o código ficaria assim.
object instancia = 3;
var resultado = instancia switch
{
string => "É um tipo string",
int => "É um tipo inteiro",
null => "É nulo",
_ => "Outro tipo"
}
// resultado: É um tipo inteiro
Declaration Pattern
Cria uma variável local com o tipo testado caso um seja igual ao outro.
Exemplo sem Declaration Pattern.
if (video.GetType() == typeof(VimeoProvider))
{
var videoProvider = video as VimeoProvider;
if (videoProvider.ContaAtiva)
{ }
}
Exemplo com Declaration Pattern.
if (video is VideoProvider videoProvider)
{
if (videoProvider.ContaAtiva)
{ }
}
Relational Patterns
Usa os operadores relacionais (<, >, <=, >=) para verificar se o valor é maior ou menor que outro valor.
object valor = "Ola Mundo";
var resultado = valor switch
{
< 3 => "Menor que três",
>= 7 => "Maior ou igual a sete",
double.NaN => "NaN",
_ => "Outro valor"
};
// resultado: NaN
Logical Patterns
Usa os operadores lógicos (not, and, or) para verificar o valor de entrada.
var resultado = 300 switch
{
< 10 and not 5=> $"Menor que dez e não pode cinco",
>= 10 and < 20 => "Maior ou igual a dez e menor que vinte",
30 or 40 => "Trinta ou quarenta",
var valor => $"Outro valor: {valor}"
};
// resultado: Outro valor: 300
Property Patterns
Acessa as propriedades ou atributos de um objeto que será testado.
record Dimensoes(int Peso, int Profundidade, int Largura, int Altura);
record Produto(decimal Preco, Dimensoes Dimensoes, string Marca);
var resultado = new Produto(60.3m, new(2700, 5, 8, 90), "Amazon") switch
{
{ Marca: "Apple" } => "Produto criado pela Apple",
{ Preco: < 50 } => "Produto barato, entrega em 15 dias",
{ Preco: >= 50 and < 100 } p => $"Produto {p.Marca} com entrega em 7 dias",
{ Preco: >= 100 and < 200 } p => $"Produto {p.Marca} com entrega em 2 dias",
{ Dimensoes: { Peso: > 1000 } and { Altura: >= 60 } } => "Produto pesado e alto",
var p => $"Dimensões do produto: {p.Dimensoes.Profundidade} x {p.Dimensoes.Largura} x {p.Dimensoes.Altura}"
};
// resultado: Produto Amazon com entrega em 7 dias
Positional Pattern
Verifica se a posição dos valores de entrada são as mesmas das condições definidas.
Usa descontructor
para obter as posições a serem testadas.
if ((true, 2) is not (true, 6))
{
Console.WriteLine("O valor das tuplas não são iguais");
}
O exemplo abaixo é rico por usar Positional Pattern com alguns padrões já mostrados anteriormente.
var resultado = (false, 0) switch
{
(false, 0) res => $"Primeiro valor é {res.Item1} e o segundo é {res.Item2}",
(true, 2) => "True e Dois",
(_, 3) => "Segundo valor é três",
(_, > 10) => "Segundo valor é maior que Dez",
var (x, y) => $"Fallback: {x} e {y}",
};
Parenthesized Pattern
Da ênfase ou altera a precedência lógica.
object valor = 6;
if (valor is not (float or double))
{ }