Mais um conceito das linguagens de programação funcional está trilhando seu caminho para o C# e o VB. Conhecido como pattern matching (correspondência de padrões), à primeira vista se parece com um bloco switch/select, contudo este técnica é muito mais ampla e poderosa do que se imagina.
Nota: Como a versão da especificação em VB ainda não está disponível, muitos destes exemplos são da especificação de correspondência de padrões para C#.
O operador is ou Matches
No núcleo do suporte à correspondência de padrões no .NET está o operador "is/Matches". Este operador bastante incomum, quebra uma classe em suas partes constituintes. Aqui está um exemplo baseado na classe de registro Cartesiano, relatada neste artigo:
public static bool operator is(Cartesian c, out double x, out double y) x = c.X; y = c.Y; return true;
Este operador não se limita apenas ao tipo proprietário. O próximo exemplo, mostra a definição do operador de uma forma em que ele permite decompor um objeto Cartesiano como se ele fosse um objeto Polar.
public static class Polar { public static bool operator is( Cartesian c, out double R, out double Theta) { R = Math.Sqrt(c.X*c.X + c.Y*c.Y); Theta = Math.Atan2(c.Y, c.X); return c.X != 0 || c.Y != 0; } } var c = Cartesian(3, 4); if (c is Polar(var R, *)) Console.WriteLine(R);
Padrões de Tipos
O padrão mais simples é o padrão de tipo, que essencialmente é um try-cast com uma atribuição. Aqui está um exemplo:
if (expr is Type v) { // code using v }
Padrão Recursivo
A maioria dos padrões será recursiva. Ou seja, eles serão formados de outros padrões, frequentemente mais simples. Veja este exemplo:
var a = new Location(1, 2, 3); //x=1, y=2, z=3 if (a is Location(1, var y, *))
Este é um padrão recursivo que consiste de um padrão constante, um padrão var e um padrão de caracter coringa.
Padrão Constante
Ocorre quando uma correspondência é feita entre uma propriedade e um valor constante. Padrões constantes usam object.Equals(left, right) para avaliar se existe uma correspondência.
Padrão Var
O padrão var é sempre considerado uma correspondência. A variável associada é populada com o valor proveniente do operador is. O tipo desta variável é o tipo estaticamente definido da expressão.
Padrão de caracter coringa
O padrão de caracter coringa é essencialmente o padrão var, exceto que você não se interessa pelo resultado.
Como ele funciona
Continuando com nosso exemplo de localização, os passos que o compilador vai realizar serão semelhantes a:
- Criar variáveis $x, $y e $z
- Chamar Location.is(a, out $x, out $y, out $z) e verificar que ele retorna true
- Padrão contante: Verificar que object.Equals($x, 1)
- Padrão var: Definir y = $y
- Padrão de caracter coringa: Ignorar $z
Blocos Switch/Select Case
Blocos switch serão ampliados para utilizar correspondência de padrões. Essencialmente, isso significa que você pode escrever instruções como:
case null: case String s case Location(1, var y, *)
Limitações
No contexto do projeto de especificação atual, não há suporte para verificação de faixas de valores (ranges). Isso significa que você não pode escrever padrões como "a is Location( > 0, 1 to 5, <= 10)". Também não há suporte para correspondência de elementos em uma lista ou enumerador.
Considerações finais
O .NET framework já disponibiliza alguns aspectos de linguagens funcionais desde sua versão 3.0, mas esse conjunto de aspectos está sendo ampliado com essa nova especificação, ainda em desenvolvimento.
A introdução destas construções na linguagem poderá simplificar ainda mais a vida de programadores que pretendem escrever código em estilo funcional em uma linguagem imperativa como C#.