Что полезного в новых версиях C#
Вместе с экспертами Ozon Tech и Route 256 подготовили обзор полезных и неочевидных фич в новых версиях языка С# за последние четыре года.
10К открытий13К показов
С# продолжает развиваться, и в новых версиях команда разработчиков языка регулярно добавляет в него новые функции и возможности. Вместе с экспертами Ozon Tech и Route 256 подготовили обзор полезных и неочевидных фич, которые появились в языке за последние четыре года. Это не тот C#, который вы учили пять лет назад.
- История версий C#
- Операторы верхнего уровня — программы без Main методов
- Nullable-типы
- Типы записи и записи структуры
- Индексы и диапазоны
- Паттерн-матчинг
История версий C#
C# был придуман в Microsoft под руководством Андерса Хейлсберга, который до этого работал над языком Delphi. Язык создавался как конкурент Java со свойствами и событиями.
Первая версия C# увидела свет в 2002 году. Затем каждые два или три года Microsoft выпускал новую версию языка. В C# постепенно добавили обобщённые типы и итераторы, Linq, фичи функциональных языков программирования, ключевое слово dynamic для упрощения работы с COM, интеграции с динамическими языками на платформе .NET, async\await, кортежи и другое.
Начиная с C# 8, разработчики выпускают новую версию языка ежегодно. В них уже нет масштабных изменений, вроде введения Linq или async\await, а некоторые фичи, такие как паттерн-матчинг, развиваются от релиза к релизу. К сожалению, это приводит к тому, что на момент появления многие фичи просто не используются. Программисты, которые изучали C# по книжкам и статьям пятилетней давности, даже не подозревают об их существовании.
В этой статье попытаемся восполнить этот пробел и опишем важные изменения в языке, которые вам стоит использовать в своих программах.
Операторы верхнего уровня — программы без Main методов
Все языки, унаследовавшие синтаксис от языка C, страдают многословностью. C# не стал исключением и перенял у Java необходимость писать следующим образом:
В C# 9 появилась фича Top-level statements. Теперь тот же самый код возможно переписать короче:
Более того, используя implicit usings, using System
писать необязательно. Компилятор автоматически добавляет юзинги для популярных пространств имён. Начиная с .NET 6 и C# 10 новое консольное приложение выглядит так:
Если вам не подходит автоматика, то можете глобально задать юзинги в отдельном .cs файле или в файле проекта.
Код, написанный таким образом, «под капотом» заворачивается в функцию Main. Все функции, которые вы определите, станут локальными и смогут использовать переменные, определённые ранее. Определять типы можно в том же файле, но только после всех top-level statements.
Nullable-типы
В C# 8 появилась фича — Nullable Reference Types, аналогичная Nullable Value Types. В проектах, созданных до .NET 6, фича была отключена по умолчанию. Она работает по следующим правилам:
- Если есть переменная или параметр ссылочного типа T, то ему необходимо присвоить значение, не являющееся null (non-nullable).
- Чтобы присвоить null, нужно поставить знак вопроса после имени типа — T? (nullable).
- Чтобы привести T? к T, необходимо использовать оператор ! после nullable выражения, иначе компилятор выдает ошибку.
- При обращении к членам T? компилятор статически проверяет, что значение ссылки не равно null.
В отличие от Nullable Value Types, string
и string?
— не разные типы. Это один и тот же тип string
, подсказка компилятору и набор атрибутов, помогающих проводить статический анализ.
Как следствие, Nullable Reference Types не даёт гарантий за пределами вашего кода. Если в коде функция публичного интерфейса класса принимает string
, а не string?
, то вам необходимо написать проверку на null
, хоть она и выглядит избыточной.
Компилятор, в свою очередь, при нарушении правил Nullable Reference Types генерирует предупреждения, а не ошибки. Но это легко исправить, указав в свойствах проекта, что Nullable предупреждения стоит воспринимать как ошибки.
Важная вещь, которую стоит помнить, — оператор var
всегда создаёт переменную nullable reference типа.
Типы записи и записи структуры в C#
Как появились
Анонимные типы добавили ещё в третью версию C#. Они неизменяемые, имеют структурную, а не ссылочную эквивалентность, и удобное представление в ToString().
Анонимные типы очень понравились программистам: они удобно описывают структуры данных, их можно использовать как ключи для словарей и группировки данных. Но у анонимных типов есть недостатки: их нельзя принимать в качестве параметров методов, нельзя создать поля класса анонимного типа, а также в анонимных типах не получится реализовать интерфейсы.
Такие типы можно создать и «руками», но вам быстро надоест каждый раз переопределять Equals
, GetHashCode
и ToString
, и вы начнёте искать более простые способы.
Один из них — кортежи, которые появились в C# 7.0. Кортежи можно красиво использовать в варианте с именами полей:
Кортежи тоже имеют структурную эквивалентность и удобное представление в ToString(). Но в отличие от анонимных типов кортежи изменяемые.
Однако, как и Nullable Reference Types, имена кортежей — магия компилятора. «Под капотом» там всегда System.ValueTuple
, поля Item1..ItemN
, и никакие интерфейсы реализовать не получится.
Как создавать
Начиная с C# 8, можно создавать свои типы, которые объединяют фичи кортежей и анонимных типов, но при этом могут иметь методы и реализовывать интерфейсы. Такие типы называются записями, а для их создания используется ключевое слово record
.
Тип, объявленный как record
, автоматически реализует деконструктор (не путать с деструктором), Equals
, GetHashCode
, ToString
, а также интерфейс IEquatable
и перегружает операторы сравнения. Свойства FirstName и LastName будут неизменяемыми. В остальном это обычный класс. Он может содержать методы, наследовать другие классы, реализовывать интерфейсы.
В примере выше используется фича из C# 9 — Target-typed new operator, когда после new не нужно использовать имя типа, если выражение присваивается переменной или параметру этого типа. Это облегчает использование Nullable Reference Types.
Начиная с C# 11 можно создать тип запись, которая является полным эквивалентом анонимного класса:
В отличие от предыдущего варианта, при таком объявлении деконструкция не будет работать, а имена свойств придётся писать каждый раз при создании экземпляра:
Позиционные параметры и обычные свойства записей можно комбинировать. Обычные свойства легко сделать необязательными и даже изменяемыми.
Записи структуры
В C#10 добавили возможность создавать записи, которые являются структурами, а не классами. В отличие от записей классов записи структуры по умолчанию изменяемы.
Если вам потребуются неизменяемые записи структуры, добавьте ключевое слово readonly, как и для обычных структур.
Оператор with
Для работы с неизменяемыми типами данных в языке появился оператор with
. Он копирует запись и изменяет значения свойств.
Оператор with
работает не только с типами записями, но и с readonly структурами и анонимными типами.
Индексы и диапазоны в C#
В C# 8 разработчики языка добавили возможность писать следующим образом:
Аналогично можно обратиться ко второму элементу с конца: ^2, к третьему: ^3 и так далее.
Кроме того, появилась возможность извлекать подмассив из массива с помощью индексов, как в языках F#, Python и многих других:
Обратите внимание: в операторе диапазона левая граница включается в диапазон, а правая — нет. В диапазонах можно не указывать левую или правую границу, или не указывать обе. В качестве границ могут быть указаны индексы как с конца, так и сначала.
Под капотом оператор диапазона превращается в структуру System.Range:
Индексы работают с коллекциями, у которых есть свойство-индексатор с целыми индексами и свойство Length или Count. Диапазоны требуют наличие свойств Length или Count и метода Slice с двумя аргументами. Например, вы можете получить подстроку из строки с помощью оператора диапазона:
Большинство типов-коллекций автоматически получит поддержку индексов, а для диапазонов нужно будет реализовать метод Slice. Кроме того, вы можете определить свои свойства и методы, которые работают со структурами System.Range
и System.Index
.
Паттерн-матчинг
Паттерн-матчинг, или сопоставление с шаблоном, — это обобщённое название множества фич, добавленных в компилятор. Попробуем разобрать их все от простого к сложному.
is на стероидах
Оператор is существует с первой версии языка C# и используется для проверки переменной на соответствие определённому типу. До сих пор в статьях и книгах по C# можно встретить такой код:
В современном C# нет необходимости проверять тип и приводить к нему в два действия. Можно просто использовать паттерн-матчинг:
Более того, в C# 11 в паттерн-матчинг можно внести вырезание строки:
Как это читать: выражение после is
говорит, что переменная x
должна быть строкой (паттерн типа) и может быть разложена на три подстроки (паттерн списка):
— один символ в начале, который мы в дальнейшем никак не используем, так как указали подчёркивание вместо имени переменной (паттерн отбрасывания значения);
— один символ в конце, который мы также игнорируем, а подстроку из любого количества символов в середине присваиваем переменной s
и просим компилятор вывести её тип (var-паттерн).
Ключевое слово and
создает логический паттерн, когда переменная должна соответствовать обоим паттернам слева и справа от and
.
В этом случае, если строка содержит меньше двух символов, то выражение is
вычисляется как false
и Console.WriteLine
не будет выполнено. Если нам хочется, чтобы подстрока s
имела длину больше нуля, то необходимо написать ещё одно условие:
Теперь мы проверяем, чтобы переменная s
, полученная в результате сопоставления с шаблоном, соответствовала паттерну (паттерн свойства): свойство Length должно иметь значение больше 0 (паттерн сравнения). Здесь важно не путать логические паттерны и булевы операторы. Это не одно и то же, и они не взаимозаменяемы.
То же самое можно записать в виде логического паттерна внутри лист-паттерна:
На этом примере становится понятно, что сложные логические условия можно записать компактно. Однако читаемость таких выражений сопоставления с шаблоном падает очень быстро.
switch и паттерн-матчинг
В выражениях case
оператора switсh
также можно использовать паттерны:
В примере выше используются паттерн деконструкции (в документации «позиционный паттерн») и паттерны сравнения.
В C# 8 ввели switch expression с более компактным синтаксисом, похожим на функциональные языки:
В этом и предыдущем примере используется case guard — произвольное булево выражение после ключевого слова when. Оно необходимо в случаях, когда мы не можем выразить необходимые условия в паттернах.
switch expression удобно использовать для создания машины состояний. На одном из форумов была такая задача: для последовательности цифр требуется убедиться, что 1 есть как минимум один раз, 2 — только один раз, 3 — как минимум один раз, а после этого ничего быть не должно.
Компилятор проверяет, что паттерны покрывают всё пространство входных значений, и если это не так — выдаёт предупреждение. Если паттерн-матчинг не смог найти подходящий паттерн, в рантайме будет ошибка. Как превратить предупреждения в ошибку компиляции вы уже знаете.
Проверка на null
После появления паттерн-матчинга проверку на равенство ссылки null
необходимо проделывать следующим образом:
Это связано с тем, что перегруженные операторы == и != могут давать ошибки если левосторонний аргумент равен null
.
Заключение
Мы рассмотрели неполный список нововведений, появившихся в новых версиях С#, начиная восьмой версии языка. Однако затронули наиболее важные из них, которые влияют на то, как мы пишем программы.
Чтобы поддерживать в форме свой уровень языка, советуем ознакомиться с историей версий C# и прочитать руководство по языку — скорее всего, узнаете много нового, если давно в него не заглядывали.
А тем, кому хочется большего, рекомендуем курс Route 256 от Ozon Tech по современным технологиям C#.
Программа рассчитана на разработчиков с опытом от 3 лет. Преподаватели и тьюторы — инженеры Ozon Tech. За два месяца вы сможете подтянуть скилы и освежить знания. На курсе вы научитесь создавать и настраивать микросервисы на ASP.NET Core, эффективно работать с асинхронным кодом, проектировать сложные распределенные системы, создавать REST и gRPC API и другое.
Надеемся, статься оказалась для вас полезной. Остались вопросы? Задайте их в комментариях.
Реклама ООО «Озон технологии» LjN8K484i
10К открытий13К показов