Nowości w C# 7, 7.1 i przedsmak C# 8

Od premiery C# 7 minęło już trochę czasu, jednak nie miałem jeszcze okazji za bardzo korzystać z wielu nowych funkcji. Ostatnio wraz z aktualizacją Visual Studio 2017 do wersji 15.3 dostaliśmy także w swoje ręce C# 7.1. Postanowiłem więc sprawdzić, co nowego dla nas przygotowano, a także co czeka nas w przyszłości.

C# 7 – krotki (tuples)

Wreszcie mamy używalne krotki (czyli tuples). Nie musimy już tworzyć nowych instancji przez Tuple.Create, teraz wystarczą nawiasy:

Możemy zwracać kilka wartości na raz, a następnie przypisać je od razu do kilku zmiennych. Typ w przypisaniu można podać na zewnątrz nawisów, tak jak tutaj var lub wewnątrz przy każdej zmiennej , np. (int sum, int diff).

Możemy też podać jedną nazwę zmiennej:

Lecz wtedy dostaniemy automatycznie stworzone calculations.Item1 oraz calculations.Item2, a tego raczej nikt nie chce. Lepiej więc podawać nazwę kroki i jej pól:

Możemy to też zrobić po prawej stronie:

Do nowych krotek potrzebujemy typu System.ValueTuple, który jest dostępny na nugecie. Warto też wspomnieć, że nowe krotki to pod spodem struktury i do tego zmienne. Możemy więc przypisać nową wartość do pól krotki, co było wcześniej niemożliwe.

C# 7 – odrzuty (discards)

Jest to coś, co głównie będzie się używać z nowymi krotkami. Teraz możemy oznaczyć zmienną podkreślnikiem _, jeśli nie potrzebujemy tej wartości.

Tutaj zamieniliśmy zmienną diff z poprzedniego przykładu na _ – do _ nie można się odwołać, jest to po prostu taki odrzut, jak sama nazwa wskazuje.

Składni tej możemy też używać w innych przypadkach, jak np. z funkcjami przyjmującymi parametr out.

C# 7 – zmienne out

Obecnie możemy deklarować zmienne out od razu w funkcji. Wcześniej trzeba było to robić jako oddzielna zmienna przed funkcją.

Teraz wygląda to tak:

Jak widzicie można używać nawet var. Niby tylko jedna linijka mniej, ale zawsze mi to przeszkadzało.

C# 7 – dopasowanie do wzorca (pattern matching)

Dopasowanie do wzorca pozwala nam zaoszczędzić trochę miejsca przy wyrażeniu is:

Teraz od razu w warunku możemy określić nazwę zmiennej. Nie musimy w środku if’a tworzyć przypisania var square = shape as Square.

Możemy też użyć dopasowania w instrukcji switch:

Sprawdzamy tu np. czy nasz item jest typu int i od razu nadajemy mu nazwę. Możemy też dodawać warunki lub używać odrzutów (discards) jak przy case'ach z tablicą object.

C# 7 – funkcje lokalne (local functions)

Od teraz możemy tworzyć funkcje w funkcjach. Czasami używamy funkcji prywatnych tylko w jednym miejscu w klasie. W takiej sytuacji funkcja lokalna mogłaby zwiększyć czytelność kodu, bo od razu byłoby widać, że odnosi się ona tylko do tej jednej metody. Dodatkowo możemy też teraz w ładny sposób wydzielić długie warunki w wyrażeniach lambda. Przed zmianami:

I po zmianach:

C# 7 – więcej expression-bodied members

W C# 6 dostaliśmy tę wygodną składnię dla funkcji i właściwości read-only, teraz dochodzą do tego także konstruktory, destruktory oraz sekcja set we właściwościach i indexerach.

C# 7 – uogólniony typ zwracany przez funkcje asynchroniczne

Do tej pory funkcje async mogły zwracać void, Task lub Task<T>. Teraz mogą być to też inne typy. Jako jeden przykładowy dodany został do .Neta ValueTask, który jest strukturą i w pewnych sytuacjach może być bardziej wydajny. Na tę chwilę potrzeby jest do niego pakiet z nugeta System.Threading.Tasks.Extensions.

C# 7 – inne

Jest jeszcze kilka mniejszych nowości jak np. zapis licz w postaci binarnej 0b0001, czy separator cyfr w stałych 0b0001_0000. Pełną listę z większą liczbą przykładów można znaleźć w dokumentacji.

Przejdźmy teraz do nowości w C# 7.1. Nie ma ich dużo i nie są tak znaczące jak w C# 7, jest to sporo mniejsza aktualizacja. Żeby ich użyć musimy ręcznie przełączyć projekt na C# 7.1. Możemy to zrobić w ustawieniach projektu -> Build -> Advanced -> Language version -> wybrać C# 7.1. Przy niektórych z poniższych funkcji Visual Studio sam zaproponuje tę zmianę.

C# 7.1 – default

Od teraz możemy napisać po prostu słówko default, żeby użyć domyślnej wartości danego typu. Wcześniej trzeba było podawać także typ, o który nam chodzi default(int).

C# 7.1 – async Task Main

Funkcja wejściowa Main() może teraz mieć w deklaracji async Task, a więc można używać w niej awaita.

C# 7.1 – dopasowanie do wzorca (pattern matching) dla typów generycznych

W C# 7 twórcy zapomnieli o tym przypadku i jest to naprawione w kolejnej wersji języka. Gdy chcemy użyć dopasowania do wzorca na typie generycznym w C# 7, program się nie skompiluje.

C# 7.1 – domyślne nazwy pól w krotkach

W C# 7 nazwy pól trzeba było nadawać otwarcie:

Jednak skoro mamy już zmienne, które nazywają się tak samo, to po co pisać to drugi raz. Do takiego wniosku doszli też twórcy języka i obecnie nazwy te są dodawane z automatu:

C# 8 – co nas czeka?

O C# 8 jeszcze wszystkiego nie wiadomo, ale mamy tu 2 prawdopodobne nowości, które wzbudziły moje zainteresowanie:

  • Wszystkie typy referencyjne będą domyślnie nie-nullowe. Oznacza to, że string, czy obiekt naszej klasy nie będzie mógł być nullem, podobnie jak teraz typy proste (jak int). Oczywiście mamy nadal do dyspozycji T? i musielibyśmy używać takiej składni, jeśli typ nullowy byłby potrzebny.
    .

    .

  • Implementacje w interfejsach. Można by pomyśleć, że jest to niepotrzebne, bo przecież od tego są klasy abstrakcyjne. Różnica jest taka, że klasa bazowa może być jedna, a interfejsów wiele. Co więcej, jeśli dodamy metodę do interfejsu, musimy ją zaimplementować we wszystkich klasach – z domyślna implementacją w interfejsie nie trzeba będzie tego robić. Jednak z tego co słyszałem, chodzi tutaj też o Xamarina. W Javie i Swifcie taka funkcjonalność istnieje, więc byłoby łatwiej portować API z Androida i iOSa.
    .

    .

Na koniec zostawię jeszcze kilka linków:

Posted in C#