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 (jakint
). Oczywiście mamy nadal do dyspozycjiT?
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:
- Dokumentacja
- The future of C# (wideo z Build 2017) – omawiają tutaj nowości z C# i Visual Studio