Zmiany w C# 9.0

Wprowadzenie

W ubiegłym tygodniu przygotowałem ogólne podsumowanie zmian w .NET 5. Natomiast w tym wpisie przyszedł czas na bardziej szczegółowe zmiany w najnowszej wersji języka C# 9.0. Jest ich kilka, a największą jest wprowadzenie rekordów.

Dla każdej zmiany przygotowałem interaktywny przykład, który możesz uruchomić bezpośrednio z poziomu bloga. Dzięki czemu szybko sprawdzisz, jak zachowa się kod aplikacji. Gorąco zachęcam do zmian w kodzie i własnych eksperymentów!

Top-level statements

Zanim przejdę do omawiania rekordów, chciałbym w pierwszej kolejności przedstawić nową funkcjonalność, która spowoduje, że w dalszej części wpisu ilość kodu w przykładach będzie sporo mniejsza.

Top-level statements, o którym mowa, minimalizuje ilość zbędnego kodu, który znajdował się w klasie Program i prawie zawsze był taki sam.

Zmianę najlepiej widać na przykładzie klasycznego Hello World!. Wersja sprzed nowego C#:

W C# 9.0 sprowadza się do tego:

Jak widać, zbędnym stał się cały kod odpowiedzialny za definicję przestrzeni nazw, klasy Program oraz metody Main. Kod tak naprawdę trafia do metody Main i w ten sposób jest uruchamiany.

W codziennej pracy ta zmiana nie wprowadza za dużo. Ale w przypadku wszelkich materiałów edukacyjnych (jak ten blog) myślę, że wyjdzie na plus 🙂

Darmowy kurs Visual Studio

Pracując z setkami programistów, zauważyłem, że większość osób nie pracuje efektywnie w Visual Studio. W skrajnych przypadkach korzystali z kopiowania z wykorzystaniem menu Edit. Wiem, że to dziwne, ale naprawdę niektórzy tak pracują. Dlatego postanowiłem stworzyć kurs Visual Studio – aby pomóc koleżankom i kolegom w efektywniejszej pracy.

Przygotowałem 30 lekcji e-mail, w których pokażę Ci, w jaki sposób pracować efektywniej i szybciej w Visual Studio. Poznasz dodatki, bez których nie wyobrażam sobie pracy w tym IDE.

Po więcej informacji zapraszam na dedykowaną stronę kursu: Darmowy Kurs Visual Studio.

Quiz C#

Ostatnio przygotowałem również quiz C#, w którym możesz sprawdzić swoją wiedzę. Podejmiesz wyzwanie?

Rekordy w C# 9.0

W C# typy dzielimy na dwa rodzaje: referencyjne oraz wartościowe. Typami referencyjnymi są klasy oraz typy anonimowe. Natomiast struktura oraz krotka są typami wartościowymi. Od C# 9.0 typy referencyjne wzbogaciły się o nowy rodzaj typów, jakim są rekordy.

A czym są rekordy? W skrócie są niezmiennymi typami referencyjnymi (immutable reference types), czyli taka klasa, której zmiana powoduje utworzenie nowego obiektu w pamięci. Dodatkowo zawiera kilka innych usprawnień, które powodują, że więcej rzeczy mamy z pudełka i nie musimy tego implementować.

Typy immutable mają sporo zalet, chociażby w programowaniu wielowątkowym, ponieważ odpada nam problem, w którym jeden wątek zmienia obiekt w trakcie, gdy inny go odczytuje.

Przed C# 9.0 byliśmy w stanie na poziomie kodu i odpowiedniej struktury osiągnąć niezmienniczość obiektów. Natomiast teraz mamy to już na poziomie samego języka.

Definicja samego rekordu sprowadza się do zmiany słowa kluczowego class na record:

Na powyższym przykładzie widać również kolejną ciekawą zmianę w rekordach w stosunku do klas. Została nadpisana metoda ToString. W przypadku klas metoda zwraca nazwę typu (wraz z namespace). Natomiast w rekordach poza nazwą widzimy jeszcze wartości poszczególnych właściwości.

Osobiście ta zmiana bardzo mnie cieszy. Nie będę musiał się męczyć i kombinować, w jaki sposób na konsoli, czy innym miejscu łatwo zrzucić stan obiektu.

Dodatkowo w przypadku rekordu możemy jeszcze bardziej uprościć jego definicję, aby ilość kodu była mniejsza:

Możemy w pewnym sensie połączyć definicję rekordu i jego konstruktora, gdzie określamy, z jakich właściwości będzie składał się rekord.

Swoją drogą w tym momencie kod aplikacji wygląda dość ciekawie, prawda?

Zmiana rekordu

Powyżej skonstruowanej instancji rekordu nie możemy zmienić poprzez przypisanie nowej wartości do właściwości. W takiej sytuacji otrzymamy taki komunikat (BTW o init-only będzie więcej w dalszej części wpisu):

Compilation error (line 4, col 1): Init-only property or indexer 'Product.Name' can only be assigned in an object initializer, or on 'this' or 'base' in an instance constructor or an 'init' accessor.

Możemy natomiast utworzyć nową instancję rekordu na podstawie już istniejącego z wykorzystaniem słowa kluczowego with. Przekazujemy nowe wartości właściwości, a pozostałe zostaną skopiowane z pierwotnego obiektu:

W tym przypadku do rekordu dodałem drugą właściwość Price określającą cenę produktów. Drugą instancję rekordu utworzyłem na bazie pierwszego używając with i przekazując nowa wartość właściwości Name. W wyniku działania aplikacji widać, że cena produktu została ładnie skopiowana z pierwotnego obiektu.

Dodatkowo widać, że pierwszy obiekt się nie zmienił oraz obie instancje znajdują się w różnych obszarach pamięci (object.ReferenceEquals zwróciło false). Co dodatkowo pokazuje, że to dwa różne obiekty w pamięci.

Porównywanie rekordów

Kolejną ciekawą zmianą w rekordach w stosunku do klas jest porównywanie obiektów. W przypadku klas, gdy porównujemy dwa obiekty, domyślna implementacja tak naprawdę sprawdza, czy referencje obiektów są takie same, czyli czy w pamięci jest to ten sam obiekt. Co widać na przykładzie poniżej:

Mimo, że product oraz newProduct zawierają te same dane, to oba porównania zwracają false. Oczywiście w przypadku klas możemy nadpisać metodę Equals i samemu zdefinować sposób porównywania dwóch obiektów.

Natomiast w przypadku rekordów domyślne porównanie bazuje na wartościach poszczególnych właściwości, a nie na referencjach. Co ładnie widać na przykładzie:

W tym przypadku mam dwie instancje rekordów, które mają te same dane. Porównanie ich zwraca true, natomiast object.ReferenceEquals zwracając false pokazuje, że w pamięci są to dwa różne obiekty.

Kolejna fajna zmiana, która pozwala zaoszczędzić trochę czasu 🙂

Po więcej informacji o rekordach odsyłam do dokumentacji: https://docs.microsoft.com/pl-pl/dotnet/csharp/whats-new/csharp-9#record-types

Init-Only Setters

W starszych wersjach C#, gdy chcieliśmy utworzyć właściwość tylko do odczytu, to wystarczyło w automatycznej właściwości nie zdefiniować settera. Wartość właściwości mogliśmy określić w konstruktorze, a później była ona tylko do odczytu.

W przypadku takiego podejście nie mogliśmy skorzystać z property initializers, czyli ustawienie wartości właściwości w klamerkach zaraz po wywołaniu konstruktora. Co widać na przykładzie (wystarczy odkomentować zakomentowany kod):

https://dotnetfiddle.net/F7VKkL

Takie zachowanie w przypadku klas, zawierających sporo właściwości, które mogą być ustawiane w dowolnej konfiguracji (nie wszystkie właściwości muszą być ustawione) jest troszeczkę upierdliwe.

W C# Microsoft postanowił to ułatwić poprzez wprowadzenie Init-Only Setter. Rozwiązanie jest bardzo proste. Wystarczy użyć słowo kluczowe init w takiej właściwości, aby cieszyć się właściwością tylko do odczytu, którą będziemy mogli ustawić w klamerkach podczas tworzenia nowej instancji obiektu:

Niby mała rzecz, a potrafi trochę ułatwić pracę.

Właściwości w rekordach są właśnie Init-Only, co było widać w powyższym komunikacie.

Usprawnienia w patter matching

C# 9.0 to również kolejne usprawnienia w patter matching. Pojawiły się warunki logiczne and, or, not. Dzięki czemu możemy tworzyć bardziej rozbudowane warunki. Dodatkowo możemy używać nawiasów i przedziałów w warunkach (np. >, >= i tak dalej).

W efekcie możemy napisać coś takiego:

Powyższy kod służy do obliczenia ceny dostawy produktu na podstawie jego wagi. Prawda, że jest zwięzły i czytelny?

Inne drobne zmiany

Dawno temu w C# 3.0 Microsoft dodał słowo kluczowe var, które możemy wykorzystywać podczas tworzenia zmiennych. Oznacza ono tyle, że kompilator sam na podstawie instrukcji określi sobie typ zmiennej. Na przykład Product poniżej:

var product = new Product();

Od C# 9.0 możemy pójść w drugą stronę. Czyli gdy znamy tym zmiennej możemy pominąć typ przy operatorze new:

Kolejna drobna zmiana, która powoduje, że piszemy trochę mniej kodu.

To byłoby na tyle, jeśli chodzi o moje podsumowanie zmian w C#. Listę wszystkich zmian znajdziesz w dokumentacji: https://docs.microsoft.com/pl-pl/dotnet/csharp/whats-new/csharp-9

Podsumowanie zmian w C# 9.0

Kolejna wersja C# przynosi dalszą ewolucję tego języka. Co może tylko cieszyć, że nie stoimy w miejscu. Za sprawą rekordów mam wrażenie, że C# 9.0 będzie jedną z bardziej rewolucyjnych wersji tego języka.

Z drugiej strony zauważyłem, że szybkość rozwoju języka (ostatnio w praktyce co roku pojawia się coś nowego) powoduje, że nie każdy programista za tym nadąża. Szczególnie, gdy utknął w jakimś starym projekcie w .NET Framework 4.0 🙂

A co Ty myślisz o zmianach z C# 9.0?

Szkolenie C# i .NET 5

Szkolenie C# i .NET 5

Zainteresował Ciebie ten temat? A może chcesz więcej? Jak tak to zapraszam na moje autorskie szkolenie o C# oraz .NET.

13 thoughts on “Zmiany w C# 9.0

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.