Mateusz Leszner | Rozwój oprogramowania | 21.09.2022
Co roku pojawia się nowa wersja języka programowania C#. Wersja C# 11 ujrzy światło dzienne w listopadzie 2022 roku wraz z .NET 7. W tym artykule przyjrzę się wybranym ważnym funkcjonalnościom C# 11. Czekasz na tę aktualizację i zastanawiasz się, co nowego się pojawi? Zapoznaj się z moim miniprzewodnikiem i sprawdź, czy nowe funkcje są warte uwagi i w jaki sposób mogą usprawnić pracę programistów.
Przejdź do:
Wydaje się, że nie tak dawno witaliśmy .NET 6 i C# 10, a już niebawem, w listopadzie 2022 roku, pojawi się kolejna aktualizacja. Jesteś ciekaw, jakie nowe funkcje przyniesie najnowsza wersja C#? Każdy programista może przekonać się sam za pomocą Visual Studio 2022 w wersji 17.3 (niektóre z nowych funkcji są dostępne we wcześniejszych wersjach VS – np. Visual Studio 17.1). Nowości można również zobaczyć w aplikacji .NET 7 SDK w wersji zapoznawczej (do pobrania na platformę .NET).
Visual Studio 2022 – features for preview
17.3
17.2
17.1
Source: Microsoft
Poniżej znajdziesz przegląd, moim zdaniem, najciekawszych z nich.
Wersja C# 11 została wyposażona w funkcjonalność generic attributes. Pozwala ona utworzyć klasę ogólną wywodzącą się z klasy System.Attribute, która ułatwia tworzenie atrybutów wymagających parametru System.Type. We wcześniejszych wersjach C# programista musiał tworzyć bardziej złożone rozwiązania w zakresie atrybutów z parametrem Type w konstruktorze. Przykład:
public class TypeAttribute : Attribute { public TypeAttribute (Type t) => ParamType = t; public Type ParamType {get; } } [TypeAttribute(typeof(string))] public string Method () => default;
W przypadku C# 11 można to osiągnąć w prostszy sposób:
public class GenericAttribute : Attribute { } [GenericAttribute ()] public string Method () => default;
Usunięcie obowiązkowego użycia typeof(…) w atrybutach i zastąpienie go typami rodzajowymi eliminuje problem związany z zapewnieniem, że typ wewnątrz typeof() będzie spełniać wymogi atrybutów. Ta funkcja miała się ukazać we wcześniejszych wersjach C#, ale ze względu na niezgodność z narzędziami deweloperskimi Microsoft zdecydował się przełożyć datę jej premiery. Teraz jednak nadszedł jej czas!
Pod tą nazwą kryje się kilka nowych funkcji, które razem składają się na generic math support. Są to:
Ta funkcja pozwala programistom używać statycznych interfejsów API z kodu ogólnego. Pozwala np. w jednej implementacji wdrożyć kalkulator sumy lub średniej dla różnych typów liczb. Więcej szczegółów na temat nowej składni znajdziesz w dokumentacji C#. Typy numeryczne implementują nowy interfejs INumber, który zawiera właściwości T.One i T.Zero. Można to wykorzystać do implementacji kalkulatorów sumy ogólnej lub średniej dla wielu typów liczb, zachowując zasadę DRY. Metoda ta może być również zaimplementowana jako operator +.
Można go znaleźć w następującym listingu:
T Sum< T >(T[] values) where T : INumber<T> { T result = T.Zero; foreach (var value in values) { result += value; } return result; } T Sum< T >(T[] values) where T : INumber<T> { var sum = Sum(values); var count = T.Zero; for (var i = 0; i < values.Length; i++) { count += T.One; } return sum / count; } var ints = new [] {1, 2, 3, 4, 5}; var doubles = new [] { 0.1, 0.7, 1.1, 8.3 }; var sumOfInts = Sum(ints); var avgOfInts = Avg(ints); var sumOfDoubles = Sum(doubles); var avgOfDoubles = Avg(doubles);
Funkcja generic math support wpłynęła na inne aspekty w C# 11:
Ta funkcja po raz pierwszy pojawiła się w C# 6 wraz z Visual Studio 2015. Zastępuje ona String.Format(). Format interpolacji ciągów znaków jest ciągiem wewnątrz cudzysłowów podwójnych, poprzedzonych $. Wewnątrz tego ciągu można określić parametry lub wykonanie metody w nawiasach {}.
$"{<interpolationExpression>[,<alignment>][:<formatString>]} other text in string”
Wraz z C# 11 funkcja zyskuje niewielką aktualizację, która pozwoli zawierać nowe linie wewnątrz formuł. Wcześniej niektóre długie formuły zawierające zapytania LINQ mogły być nieczytelne. Już tak nie będzie. Wraz z kolejnym wydaniem C# będziemy mogli używać nowych linii, a tym samym – skomplikowane formuły będą wyglądać o niebo lepiej.
var list = new [] {"Apple", "Banana", "Orange", "Grapefruit"}; var str = $"Third letters of fruits starts with B: {list .Gdzie(fruit => fruit.StartsWith("B")) .Select(fruit => fruit[2]) .FirstOrDefault()} ";
Dopasowanie wzorca – dopasowanie obiektu do typu w przypadku instrukcji wielokrotnego wyboru, ale nie tylko. Więcej informacji na temat dopasowywania wzorców można znaleźć w dokumentacji Microsoft. Dopasowanie wzorca zostało wprowadzone w C# 7 ze słowem kluczowym is pozwalającym zmapować obiekt do typu i when – słowem kluczowym do użycia w instrukcjach wielokrotnego wyboru.
Następnie C# 7.1 rozwinął wzorce typów do obsługi typów ogólnych. C# 8 ulepszył dopasowywanie wzorców, dodając wzorce właściwości pozwalające dobrać typ i właściwość. Kolejne wydanie C# przyniosło wzorce relacyjne. Zatem od wersji C# 9 możemy użyć >, <, >=, <= w dopasowywaniu właściwości. C# 9 umożliwił użycie operatorów logicznych and, or, not, is null, is not null w dopasowaniu wzorca i var – słowa kluczowego do tworzenia zmiennych. C# 10 rozszerzył jeszcze wzorce własności… a teraz kolej na możliwości, jakie oferuje C# 11.
Wzorce listy – dopasowanie sekwencji elementów. Listę można dopasować, używając niektórych elementów jako wzorca. Discard pattern (_) służy do dopasowania dowolnych pojedynczych elementów, a wzorzec zakresu (..) – do dopasowania dowolnej sekwencji zerowych lub większej liczby elementów.
var list = new [] { new [] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10},// A new [] {1, 2, 3, 5, 7, 10},// B new [] {0, 2, 3, 8, 10, 10},// C new [] {3, 4, 10},// D nowe [] {1, 4, 10, 15, 21, 3, 10},// E nowe [] {-1, 2, 3, 5, -1, 10}// F }; • Match complete list: list.Where(x => x is [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);// matches list A • Match some elements: list.Where(x => x is [_, 2, 3,_,_, 10]);// matches lists B, C, F • Match range: list.Where(x => x is [1, 2, .., 10]);// matches lists A, B • Mixed match for elements and range: list.Where(x => x is [1,_, .., 10]);// matches lists A, B, E • Match list starting with negative number: list.Where(x => x is [< 0, ..]);// matches list F
Nieprzetworzone literały ciągów mogą być wielowierszowe, zawierać cudzysłowy i inne znaki specjalne bez sekwencji ucieczki (escape) oraz interpolacje ciągów. Oto kilka przykładów:
var line2 = "line2 from another variable"; var str1 = """ line1 {line2} - won't be replaced line3 line4 line6 """; var str2 = $""" line1 from second string {line2} line3 """; var str3 = $$""" using { in line 1} {{line2}} {line2} this won't be replaced {{{line2}}} you can use it like this """ ;
Developerzy zyskują możliwość oznaczania właściwości i pól z użyciem modyfikatora required. Pozwala to upewnić się, że dane wartości są inicjowane w konstruktorze. Następnie należy ustawić atrybut SetsRequiredMembers na konstruktor, który powinien zainicjować wartości. Możesz mieć konstruktory bez atrybutu, nie muszą inicjować one wymaganych wartości.
Wymagane elementy muszą być zainicjowane, ale można je zainicjować do wartości null. W przypadku typu referencyjnego niedopuszczającego wartości null kompilator wysyła ostrzeżenie, jeśli zainicjujesz element do tej wartości. Jeśli element (member) nie jest w ogóle zainicjowany, kompilator wystawia błąd.
W C# 11 planowano dodać funkcję sprawdzania parametru null, ale została ona na razie odłożona ze względu na zastrzeżenia developerów na Spotkaniu C# Language Design, które odbyło się 6 kwietnia 2022 r. Funkcja została udostępniona do wglądu i szeroko omówiona. Podczas gdy wielu developerów było zadowolonych, inni mieli wszelkiego rodzaju zastrzeżenia.
Przed wydaniem C# 11 programiści musieli tworzyć konstruktory strukturalne o parametrach zapewniających inicjowanie wszystkich pól. C# 11 sprawi, że będzie inaczej. Na razie można utworzyć strukturę z konstruktorem bez parametrów, który nie inicjuje od razu elementów struktury. Następnie zadaniem kompilatora jest sprawić, by wszystkie jeszcze niezainicjowane pola zostały zainicjowane do ich wartości domyślnej. Kompilator dodaje niezbędny kod na początku body konstruktora, przed uzyskaniem dostępu do pól. Jeśli jest to możliwe przed zainicjowaniem wszystkich pól, struktura zostanie zainicjowana do wartości domyślnej przed wykonaniem korpusu konstruktora.
Więcej o inicjowaniu struktury i wartościach domyślnych w C# można przeczytać na stronie dokumentacji Microsoft.
public readonly struct AutoDefaultStruct { public int IntegerValue {get; } public string StringValue {get; } public double DoubleValue {get; } public AutoDefaultStruct () { //IntegerValue = default; //StringValue = default; //DoubleValue = default; // nie musi ustawiać wartości dla wszystkich pól, domyślne inicjalizacje zostaną dodane na początku konstruktora przez kompilator } } var structDemo = new AutoDefaultStruct();
Powyżej opisałem najważniejsze funkcje C# 11, które można nawet dziś sprawdzić w wersji zapoznawczej. Pełna lista zmian jest rzecz jasna znacznie dłuższa i obejmuje:
Oraz wiele więcej! Dokumentację techniczną możesz znaleźć w serwisie GitHub.
Przeczytaj także:
Dodaj komentarz: