Kolejną nowością w C# 7, jest wprowadzenie wzorców. Jest to nowość która ma być dalej rozwijana. Aktualnie można pobawić się kilkoma opcjami pattern matching które są rozszerzeniem aktualnie dostępnych konstrukcji językowych.

Z cyklu nowości w C# 7, do tej pory ukazały się artykuły:

  1. C# 7: Tuples
  2. C# 7: Pattern Matching
  3. C# 7: out i ref
  4. C# 7: Funkcje lokalne
  5. C# 7: Lukier składniowy

Side node: wszystkie przykłady zostały przetestowane na VS 2017 RC.

Znów wykorzystam klasę Person:

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

is

Do tej pory w C# mogliśmy przetestować czy dana zmienna jest określonego typu wykonując wyrażenie is:

var p = new Person { Name = "Jakub", Age = 34 };
var isPerson = p is Person;
Console.WriteLine(isPerson);

Teraz, to is po prawej stronie może posiadać wzorzec a nie tylko typ. Dzięki czemu możemy robić teraz testy typu:

object obj = null;
var isNull = obj is null;
Console.WriteLine(isNull);

Swojego też czasu, kiedy chcieliśmy sprawdzić czy coś jest danego typu to przeważnie też potem chcieliśmy to na ten typ rzutować. Z powodu wydajnościowych lepiej się to robiło z wykorzystaniem wyrażenia as:

var x = obj as Person;
if(x != null) {}

Które umożliwiało rzutowanie obiektu na dany typ, a jak się nie udało, to konwersja zwracała null. Teraz możemy to samo osiągnąć wykorzystując is:

obj = p;
if(obj is Person pp)
{
    Console.WriteLine(pp.Name);
}

Co, po pierwsze zwróci true/false i do tego wykona rzutowanie obj na Person i przypisze wynik do zmiennej p.

Można to też łączyć:

int i = 0;
object o = "34";
if(o is string s && int.TryParse(s, out i))
{
    Console.WriteLine(i);
}

A można to jeszcze bardziej ukrócić ;) ale o tym następnym razem

case

Zaktualizowany także została instrukcja switch, która teraz umożliwia

  • dopasowanie po każdym typie (nie tylko prymitywnym)
  • Wzorce w warunku case
  • Dodanie strażnika do warunku case

Jednak zmienia się też trochę podejście do instrukcji switch. Dla przykładu:

object obj = new Person() { Age = 30, Name = "Tommy" };

switch (obj)
{
    case Person p:
        Console.WriteLine(p.Name);
        break;

    case Person p when p.Age >= 30:
        Console.WriteLine("Adult, over 30: {0}", p.Name);
        break;

    default:
        Console.WriteLine("NOT A HUMAN");
        break;

}

Kolejność tutaj ma znaczenie, warunek 1 będzie wychwycony przed drugim! Na szczęście VS 2017 jest mądry i wywali błąd kompilacji oraz poinformuje nas o tym w edytorze:

Wsparcie VS i kompilatora
Wsparcie VS i kompilatora

W kolejności jedynie warunek default jest wyjątkiem – będzie on wykonany na samym końcu nie zależnie od swojego położenia. Jednak, jest przypadek kiedy default może zostać wychwycony a tego byśmy nie chcieli:

Person pp = null;

switch(pp)
{
    case Person p:
        Console.WriteLine("HUMAN");
        break;

    default:
        Console.WriteLine("NOT A HUMAN");
        break;

}

Nic nie zostanie wyłapane ze względu na null, który nie jest wychwycony dla bezpieczeństwa (jak byśmy się na przykład chcieli odwołać do wartości danego obiektu). By go wychwycić musimy dodać osobny warunek:

Person pp = null;

switch (pp)
{
    case Person p:
        Console.WriteLine("HUMAN");
        break;

    case null:
        Console.WriteLine("HUMAN?");
        // why not doing pp is Person?
        // http://stackoverflow.com/a/7640437/248489
        break;

    default:
        Console.WriteLine("NOT A HUMAN");
        break;

}

Aa no i wszystkie zmienne stworzone w warunku case mają zasięg tego warunku jak to przykład z Person p pokazał.

Podsumowanie

To tyle na dzisiaj. Fajnie, że dopasowanie wzorcowe wchodzi do C#, jestem ciekawe jak to się rozwinie w kolejnych wersjach. Jak to mówią, lepiej późno niż w cale ;)

A wam jak się podobają nowości C#? Ciekawe? Znajdziecie dla nich zastosowanie?