Neste artigo veremos os melhoramento introduzidos no recurso Pattern Matching no C# 9.0. |
Dentre as novidades do C# 9.0 temos seis mudanças principais no recurso Pattern Matching ou correspondência de padrões, aprimoramento dos padrões de tipo ou Type Pattern e outros padrões.
Mas o que vem a ser essa tal correspondência de padrões ?
A correspondência
de padrões é o ato de verificar se um determinado objeto atende a determinados
critérios.
Esses critérios podem variar de “ser uma instância de um tipo” a “ter
uma propriedade cujo valor está dentro de uma faixa de valores”.
Quando usar correspondência de padrões ?
A correspondência de padrões é excelente para atravessar estruturas de objetos
complexos quando o sistema de tipos não pode nos ajudar.
Um bom exemplo é ao explorar um objeto recebido de uma API REST externa.
Além disso, o recurso pode ser usado para criar uma máquina de estado finito.
Onde usar correspondência de padrões ?
A correspondência de padrões é uma verificação, portanto, pode ser usada sempre
que estivermos introduzindo um branch em nosso código.
Os cenários típicos são:
Atualmente a linguagem C# suporta muitos tipos de padrões: alguns são mais comuns do que outros e todos eles podem ser combinados para criar expressões muito poderosas.
Apenas para constar segue a lista dos principais padrões:
Vejamos a seguir algumas das novidades no C# 9.0 para o recurso Pattern Matching.
Nos exemplos a seguir vamos criar um projeto Console do tipo .NET Core usando o .NET Core 5.0.
Padrão Relacional ou Relational Patterns
No C# 9 você pode usar um padrão relacional que permite o uso de <,>, <= e> = em padrões como este:
using System;
namespace CShp_PatternMatching
{
public class Produto
{
public string Nome { get; set; }
public int CategoriaId { get; set; }
}
public class Livro : Produto
{
public string ISBN { get; set; }
}
public class Notebook : Produto
{
public bool TemBluetooth { get; set; }
}
class Program
{
static void Main(string[] args)
{
var produto = new Produto { Nome = "Caderno", CategoriaId = 4 };
var resultado = GetImposto(produto); // Retorna 5
Console.WriteLine($"Imposto = {resultado}");
Console.ReadLine();
}
// Padrão Relacional
private static int GetImposto(Produto p) => p.CategoriaId switch
{
1 => 0,
< 5 => 5,
> 20 => 15,
_ => 10
};
}
}
|
Veja como o padrão relacional ficou mais simples de usar.
Padrão Lógico ou Logical Pattern
O C# 9 permite que você use operadores lógicos como ‘and’, ‘or’ e ‘not’, eles podem até ser combinados com padrões relacionais como mostra o exemplo a seguir:
public class Produto { public string Nome { get; set; } public int CategoriaId { get; set; } } public class Livro : Produto
{
public string ISBN { get; set; }
}
public class Notebook : Produto
{
public bool TemBluetooth { get; set; }
}
class Program
{
static void Main(string[] args)
{
var produto = new Produto { Nome = "", CategoriaId = 4 };
var resultado = GetImposto(produto); // Retorna 5
Console.WriteLine($"Imposto = {resultado}");
Console.ReadLine();
}
// Padrão Relacional combinado com padrão lógico
private static int GetImposto(Produto p) => p.CategoriaId switch
{
0 or 1 => 0,
> 1 and < 5 => 5,
> 20 => 15,
_ => 10
};
}
|
Padrão de Negação ou Not Pattern
No C# 9, agora o operador lógico 'not'
também pode ser usado em uma instrução if e
também funciona com uma instrução ternária, como mostra o código abaixo:
public class Produto
{
public string Nome { get; set; }
public int CategoriaId { get; set; }
}
public class Livro : Produto
{
public string ISBN { get; set; }
}
public class Notebook : Produto
{
public bool TemBluetooth { get; set; }
}
class Program
{
static void Main(string[] args)
{
var produto = new Produto { Nome = "", CategoriaId = 4 };
GetDesconto(produto); // Retorna 25
GetDescontoTernario(produto); // Retorna 25
Console.ReadLine();
}
// padrão de negação (Not Pattern)
private static int GetDesconto(Produto p)
{
if (p is not Notebook)
return 25;
return 0;
}
private static int GetDescontoTernario(Produto p)
=> p is not Notebook ? 25 : 0;
}
|
Padrão de Tipo ou Type Pattern
Outra melhoria do
C# 9 com relação ao padrão de tipo foi que agora quando um tipo corresponde, o
símbolo de sublinhado _ (conhecido como
parâmetro de descarte) pode ser omitido, o que torna a sintaxe mais leve:
Vamos resolver o problema usando a API Lazy<T>:
public class Produto
{
public string Nome { get; set; }
public int CategoriaId { get; set; }
}
public class Livro : Produto
{
public string ISBN { get; set; }
}
public class Notebook : Produto
{
public bool TemBluetooth { get; set; }
}
class Program
{
static void Main(string[] args)
{
var produto = new Produto { Nome = "", CategoriaId = 4 };
GetDescontoTipoSimples(produto);
Console.ReadLine();
}
// padrão de tipo simples ou type pattern
private static int GetDescontoTipoSimples(Produto p) => p switch
{
Notebook => 0,
Livro b => 75,
_ => 25
};
}
|
No código acima, antes do C# 9.0 teríamos que escrever o código usando o padrão simples assim:
// padrão
de tipo simples ou type pattern private static int GetDescontoTipoSimples(Produto p) => p switch { Notebook _ => 0, Livro b _ => 75, _ => 25 }; |
Aqui note que
usamos o sublinhado (_).
E estamos
conversados...
"E não comuniqueis
com as obras infrutuosas das trevas, mas antes condenai-as. Porque o que eles
fazem em oculto até dizê-lo é torpe. Mas todas estas coisas se manifestam, sendo
condenadas pela luz, porque a luz tudo manifesta."
Efésios 5:11-13
Referências:
ADO .NET - Acesso Assíncrono aos dados -
C# - Programação Funcional - Exemplos
C# - Coleções Imutáveis
C# 9.0 - Apresentando Records
C# - Os 10 Erros mais comuns dos iniciantes
C# - Otimizando o código