O que há de novo no C# 7.0 - Pattern Matching
Pattern Matching é uma nova estrutura na linguagem que permite testar um valor contra um determinado formato enquanto atribui seu resultado.
Na versão 7.0 do C# são esperados três patterns:
- Constant - Testa a expressão contra uma constante, isto é, literais, variável const, o valor de um enum ou typeof
- Type - Testa se uma expressão é de um determinado tipo e em caso de sucesso faz o cast para o tipo, atribuindo o resultado em uma variável
- Var - Esta expressão sempre é executada com sucesso, servindo apenas para atribuir o valor em uma nova variável
E mais estão por vir, no GitHub é possível ler o documento sobre Pattern Matching
Atualmente estes patterns podem ser utilizados nas expressões is e no case do bloco switch.
Is Expressions
O principal uso nas expressões is será do Type Pattern, para atribuir uma variavel enquanto teste seu tipo. Por exemplo, quando é necessário executar um método de uma classe derivada, em vez de:
Dog dog = animal as Dog;
if(dog != null)
{
dog.Bark();
}
//Ou
if (animal is Dog)
{
Dog dog = (Dog)animal;
dog.Bark();
}
É possível validar e atribuir em uma unica expressão:
if (animal is Dog dog)
{
dog.Bark();
}
Como muitos dos recursos são apenas Syntactic Sugar, recomendo sempre avaliar qual a IL gerada, neste caso, é equivalente ao seguinte código C#:
Dog dog;
if ((dog = (animal as Dog)) != null)
{
dog.Bark();
}
Switch statements
Com o switch fica ainda mais interessante ao combinar as condições, mantendo as regras separadas para cada caso e o código mais claro:
Animal animal = GetAnimal();
switch (animal)
{
case Dog dog when dog.IsShortHaired:
Console.WriteLine("Short Haired");
break;
case Dog dog:
Console.WriteLine("Not Short Haired");
break;
case Cat cat:
Console.WriteLine("Cat");
break;
case null:
throw new ArgumentNullException();
default:
Console.WriteLine("Animal");
break;
}
O escopo de cada variavel atribuida no case é restrita ao bloco em que foi declarado e pode ser utilizado para validar outras regras, como no exemplo o IsShortHaired. Assim caso alguma validação não seja feita com sucesso, o bloco não é executado e o próximo case é avaliado.
Outra mudança importante é que agora o ordem dos case's é validada na compilação, impedindo que se use um case que inutilize os que estão abaixo dele, por exemplo:
E o case default será sempre executado por último, não importando o local em que foi declarado.