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 szybko 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śli 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 nadal 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ć, że dana metoda nie może zmieniać pól.
Domyślnie metody w interfejsach
Jeden przykład 1000 słów. Od teraz 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, że warto zapamiętać, że 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 właściwie 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śli dany typ nie jest zaznaczony, że 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, że dana wartość nie może przyjąć null-a.
Ostrzeżenie zniknie, jeśli damy znać kompilatorowi, że 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 kluczowe 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ć, że 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 : )