Nowości w C# 8

blogprogramisty.net 1 rok temu

Tak jak pisałem wcześniej, wole napisać o nowościach jak już je dobrze poznam więc stąd takie opóźnienie. Po drugie cześć osób i tak nie używa wszystkich nowości, bo tak gwałtownie wychodzą więc warto co jakiś czas o tym napisać aby przypomnieć sobie. Zapraszam na nowości w C# 8 , które weszły w 2019 roku. Napisałem je w skróconej wersji z małymi komentarzami aby było łatwiej je zapamiętać.

Spis treści

  • Indeksy i zakresy
  • Null-coalescing assigment
  • Deklaracje using
  • Modyfikator readonly w strukturach
  • Domyślnie metody w interfejsach
  • Wyrażenie switch
  • Tuple patterns, positional patterns i property patterns
  • Nulowalne typy referencyjne
  • Asynchroniczne strumienie

Indeksy i zakresy

Dodano ułatwienia w pracy nad elementami lub całymi zakresami wartości tablicowych.

Wprowadzony indeksy oznaczone jako ^1 jako ostatni element, ^2 jako drugi od końca element itd.

char[] sample = new char[] { 'A', 'B', 'C', 'D','E' }; var lastElement = sample[^1]; // E ostatni element var secondLastElement = sample[^2]; // D przed ostatni element var firstTwo = sample[..3]; // A B C z całej tablicy weź 3 elementy. Idź tak długo jak będziesz mieć 3 var lastThr = sample[2..]; // C D E pomiń 2 pierwsze elemeny. Pomijaj 2 elemeny a potem daj resztę var middleOne = sample[2..3]; // C pomiń 2 pierwsze elemeny ale idź tak długo jak będziesz mieć 3 elemeny (razem z tymi pominiętymi) var lastTwo = sample[^2..]; // D E daj 2 ostatnie (^) elemeny

Możemy też dzielić tablice na części tak jak sobie życzym.

Powstały też typy, w których można przechowywać te wyrażenia

Index lastElementType = ^1; Range middleOneType = 2..3;

Null-coalescing assigment

Od teraz można przypisywać do zmiennej jeżeli jest null-em w tenm sposób

List<int> test = null; //zamiast if (test == null) { test = new List<int>(); } //można użyć tego test ??= new List<int>();

Deklaracje using

Od teraz możemy w deklaracjach using zrezygnować z nawiasów i postawić na końcu średnik. Funkcja using przez cały czas będzie działać a metoda dispose wywoła się po wyjściu z danego bloku kodu. Czyli w tym przypadku po wyjściu z deklaracji if-a

public void UsingTests() { if (true) { using var fr = new StreamReader(@"C:\"); } }

Modyfikator readonly w strukturach

Od teraz możemy w metodach w strukturze zadeklarować, iż dana metoda nie może zmieniać pól.

Domyślnie metody w interfejsach

Jeden przykład 1000 słów. od dzisiaj można zdefiniować domyślnie implementacje metod w interfejsach, dzięki temu nie musimy nic zmieniać w implementacjach już istniejących w klasach.

public interface INew { void New(); int Add(int x, int y) => x + y; }

Aby jednak wykonać taką metoda trzeba ją wywołać explicitly

var test = ((INew)new NewClass()).Add(2, 3);

Można też definiować statyczne pola, które będą dostępne w danej implementacji metody w interfejsie albo…

public interface INew { void New(); int Add(int x, int y) => x + y + Handicap; static int Handicap = 3; }

…na zewnątrz poprzez interfejs.

INew.Handicap = 3;

Wyrażenie switch

Od teraz można korzystać z instrukcji switch w wyrażeniach (czyli tak jak by w jednej długiej instrukcji)

var cardNumber = 2; string name = cardNumber switch { 1 => "First", 2 => "Second", 3 => "Last", _ => "Unknown" };

Z doświadczenia wiem, iż warto zapamiętać, iż po danym case-sie, należy wstawić przecinek, jest to czasem frustrujące gdy nie chce się skompilować, bo komunikat nie zawsze jest taki oczywisty.

Tuple patterns, positional patterns i property patterns

Od teraz C# 8 wspiera trzy nowe wyrażenia, wszystkie wiążą się głownie z instrukcją switch choć nie zawsze.

Tuple patterns i positional patterns (pozwalające na dekonstruowanie wyrażeń na tuple)

var cardNumber = 2; var age = 12; string name = (cardNumber, age) switch { (1,3) => "First", (1,4) => "Second", (2,3) => "Last", _ => "Unknown" };

Positional patterns

Pozwalają sprawdzić obiekt razem z jego polami

object name = "Blog"; if (name is string { Length: 3 }) { name += "Programisty"; }

Nulowalne typy referencyjne

Od teraz można a adekwatnie to trzeba przy projekcie zdecydować jak się podchodzi o typów, które mogą być NULL-em. Można teraz zmusić kompilator do tego aby dawał ostrzeżenia jeżeli dany typ nie jest zaznaczony, iż może być nullowalny wprost.

Wygląda dość niewinnie ale w projekcie zaczyna być z tym pewnie zamieszanie. VS dużo podkreśla i co chwilę trzeba sprawdzić czy to błąd czy tylko ostrzeżenie. Czasem ciężko się domyśleć czy i dlaczego podkreśla i wysyła ostrzeżenie, bo w kodzie wygląda tak, iż dana wartość nie może przyjąć null-a.

Ostrzeżenie zniknie, jeżeli damy znać kompilatorowi, iż wiemy co robimy

Przydatny tez okazuje się operator !, który umożliwia przerwanie ciągu wywoływań kolejnych pól i metod gdy dana wartość jest null-em

Wtedy wystarczy dodać ! i po sprawie

Asynchroniczne strumienie

Od teraz można używać razem wyrażeń await w strumieniach, które zwracają elementy poprzez słowo najważniejsze yield. Wcześniej nie było takiej możliwości

public async IAsyncEnumerable<int> Range(int deley) { for (int i = 0; i < 10; i++) { await Task.Delay(deley); yield return i; } }

Podsumowanie

Podczas pisania o tych nowościach przypominało mi się, gdzie je mogłem zastosować a tego nie zrobiłem. Patrząc na to z takiej odległej perspektywy to wyraźnie widać, iż fakt jest to spory rozwój, a nie aż tak wielki jak by się mogło wydawać. Pytanie dlaczego? Może te zmiany idą w jakiś kierunku a nie koniecznie w takim w którym, najczęściej się idzie piszą programy w .NET-cie. Wiele zmian jest bardzo takich skryptowych, ułatwiających pisanie kodu zwięzłego i nie czytelnego (wyrażenie switch, potrafią być dość duże, indeksy i zakresy). Tak czy inaczej warto je znać, bo zawsze się to przydaje na…hackathon-ach : )

Idź do oryginalnego materiału