Дождались! .NET 10 вот-вот выйдет, а значит, самое время запускать фейерверки и отмечать юбилей любимой платформы! Ну и, само собой, ознакомимся с улучшениями JIT, расширением стандартной библиотеки, новыми возможностями SDK и с другими нововведениями в этой статье.

.NET 10 сфокусирован на улучшении производительности, а также безопасности — в частности, криптография подверглась серьезной доработке. Это long-term support (LTS) релиз, а значит будет поддерживаться в течение трёх лет.
Эта статья расскажет лишь о некоторых важных и интересных улучшениях в библиотеках, Runtime и SDK. Поскольку одной публикации не хватит, чтобы рассказать о всех изменениях в новом .NET 10.
Также отмечу, что мы уже работаем над поддержкой новой версии .NET 10 и C# 14 — она появится в PVS-Studio 7.40. Релиз запланирован на начало декабря, и чтобы его не пропустить, приглашаю подписаться на рассылку пресс-релизов.
C# 14
Все новшества C# 14 были разобраны в отдельной статье. Среди них extension-блоки, ключевое слово field, развитие паттерн-матчинга и много другое. Также можно выделить возможность использования оператора ? при присваивании.
До С# 14 вам требовалось проверять переменную на null для безопасного присваивания её свойству значения:
if (user is not null)
{
user.LastActive = DateTime.UtcNow;
}
Сейчас же эквивалентный код может выглядеть следующим образом:
user?.LastActive = DateTime.UtcNow;
С этим нововведением бойлерплейтного кода станет меньше, что не может не радовать.
Производительность
Каждый раз наш родной .NET становится всё быстрее и быстрее. И традиционно Стивен Тауб выпустил большую статью, в которой подробно рассказал про улучшения производительности.
В частности, изменения затронули LINQ, регулярные выражения, криптографические алгоритмы, JIT, AOT, I/O и много другое.
Чтобы быть в курсе всех изменений, рекомендую хотя бы тезисно ознакомиться со статьей.
Библиотеки
Криптография
В библиотеку System.Security.Cryptography были добавлены типы для поддержки трёх новых асимметричных постквантовых алгоритмов: ML-KEM (FIPS 203), ML-DSA (FIPS 204) и SLH-DSA (FIPS 205).
Напомню, .NET 10 — LTS версия, поэтому достаточно мудрое решение — добавлять поддержку постквантовых алгоритмов шифрования. Поскольку с каждым годом атака по принципу "собери сейчас, расшифруй потом" становится все более реальной. Речь идет о сценарии, когда злоумышленники перехватывают и хранят зашифрованные данные с целью дешифровать их в будущем, когда это станет технически возможно.
Коллекции
Были добавлены дополнительные перегрузки методов TryAdd и TryGetValue для OrderedDictionary<TKey, TValue>. Единственное отличие новых методов в том, что они вдобавок возвращают индекс в качестве out параметра.
Сериализация
Теперь можно настраивать обработку циклический ссылок при сериализации или десериализации. Сделать это можно, указав ReferenceHandler в JsonSourceGenerationOptionsAttribute.
Также представлен параметр JsonSerializerOptiops.AllowDuplicateProperties, который нужен для запрета повторения свойств JSON.
Строки
В .NET 10 появились новые API, которые расширяют нормализацию за пределы строковых типов. Существующие API работали только с типом строки, поэтому данные в других формах — таких как массивы символов или интервалы — приходилось приводить к строковому типу. Теперь в этом нет необходимости: новые API позволяют работать с диапазонами символов.
Также были добавлены методы для преобразований между последовательностью байтов UTF-8 и шестнадцатеричными представлениями без необходимости выделения промежуточных строк. Эти методы перегружают существующие реализации, которые работают только для string и ReadOnlySpan<char>, но обеспечивают более высокую производительность, так как напрямую работают с байтами в кодировке UTF-8.
Кроме того, была добавлена опция NumericOrdering, которая указывает, что сравнение строк должно производиться численно, а не лексикографически. В данном случае строка "2" будет эквивалента строке "02".
Runtime
Поддержка AVX10.2
В новой версии фреймворка была добавлена поддержка инструкций AVX10.2 для процессоров на основе x64. Напомню, процессоры с поддержкой AVX10.2 выйдут уже в следующем году, поэтому полностью пощупать новую фичу получится лишь с появлением соответствующего оборудования.
Будем ждать.
Стековая аллокация для небольших массивов
.NET 10 добавляет стековую аллокацию для маленьких массивов значимых и ссылочных типов. В данном случае, массив размещается на стеке, если JIT уверен, что данные не переживут контекст, в котором были созданы. Это решение направлено на снижение числа объектов, которые нужно отслеживать сборщику мусора. Таким образом, это снижает нагрузку на GC и открывает возможности для будущих оптимизаций.
Пример:
static void Sum()
{
int[] numbers = {1, 2, 3};
int sum = 0;
for (int i = 0; i < numbers.Length; i++)
{
sum += numbers[i];
}
Console.WriteLine(sum);
}
Массив из трех чисел numbers будет размещен на стеке, потому что компилятор знает, что эти данные не переживут вызов метода Sum. То же самое относится и к массивам ссылочных типов.
static void Print()
{
string[] words = {"Hello", "World!"};
foreach (var str in words)
{
Console.WriteLine(str);
}
}
Как и в предыдущем примере, жизненный цикл массива words ограничивается лишь методом Print, поэтому компилятор разместит этот массив на стеке.
write-barrier GC
Как вы, возможно, знаете, сборщик мусора в .NET работает по поколенческой модели, т.е. куча организуется в соответствии с тем, как долго объекты находятся в памяти. Это позволяет быстро собирать объекты определённого поколения. Однако проблема может возникнуть, если каким-то образом ссылка молодого поколения попадет в более старое, потому что при сканировании молодого поколения, старые не сканируются.
Чтобы избежать подобных ситуаций, используется механизм барьера записи, который отслеживает пересечения между поколениями.
Тем не менее, для производительности компилятора лучший барьер записи — это тот, которого вообще нет.
Так вот, .NET 10 также включает правки, которые позволяют убрать некоторые барьеры записи GC, тем самым улучшая производительность.
Улучшение JIT
.NET 10 принёс значительные улучшения для JIT-компиляции, которые направлены на повышение производительности. Среди изменений: усовершенствование инлайнинга, улучшение создания кода для элементов структур, отмена абстракции перечисления массива и многое другое.
SDK
Очистка ссылок на пакеты
Теперь, начиная с версии .NET 10, функция аудита NuGet может удалять ссылки на пакеты, которые не используются. Это нововведение также направлено на оптимизацию: уменьшается количество пакетов, которые восстанавливаются и анализируются во время процесса сборки.
Эта функция по умолчанию будет включена для всех фреймворков проекта, который нацелен на .NET 10 и последующие версии.
MSBuild
MSBuild, работающий в Visual Studio (или через msbuild.exe), является приложением .NET Framework; а MSBuild, работающий в dotnet CLI — приложением .NET. Это означает, что любые задачи MSBuild, написанные для работы в .NET, нельзя использовать при сборке в Visual Studio или при использовании msbuild.exe из-за разницы в окружении.
Начиная с .NET 10, Visual Studio 2026 и msbuild.exe будут запускать MSBuild-задачи, которые собраны для .NET. Это означает, что теперь можно использовать те же задачи как при сборке в Visual Studio, так и с помощью msbuild.exe через dotnet CLI. Это изменение позволит избежать лишних переписываний и дальнейшего поддержания MSBuild-задач под другой фреймворк.
Новые команды
Была добавлена команда dotnet tool exec, которая позволяет выполнить средство .NET без его глобальной или локальной установки — это особенно полезно для CI/CD.
Кроме того, добавлен новый скрипт dnx, который предоставляет упрощённый способ выполнения инструментов. Он перенаправляет все аргументы в интерфейс командной строки dotnetдля обработки.
С выходом .NET 10 также появляется возможность инспекции интерфейса командной строки с помощью параметра --cli-schema. При использовании он выводит представление JSON-дерева команд CLI для вызываемой команды или подкоманды.
Запуск одиночных файлов C# без проектов
Теперь можно создавать приложения на основе одиночных файлов без проекта, что упрощает создание и запуск программ. Для этого нужно использовать команду dotnet run для одиночного *.cs файла. По задумке разработчиков, такой подход найдёт применение для создания небольших служебных программ командной строки, прототипов, а также различных экспериментов.
Все приложения на основе файлов по умолчанию нацелены на native AOT-компиляцию и поддерживают публикацию в собственных исполняемых файлах с помощью dotnet publish.
Заключение
Подводя итог, можно смело заявить, что .NET 10 — это крепкая версия, достойная быть как юбилейной, так и LTS-версией. Радует, что разработчики из года в год фокусируются на улучшении производительности и поддерживают актуальность стандартной библиотеки.
В статье были приведены лишь некоторые наиболее примечательные, по нашему мнению, нововведения. С полным списком улучшений вы можете ознакомиться здесь. Также пишите в комментариях, какие важные или любопытные изменения я упустил, а также ваше мнение о .NET 10 — будет интересно почитать.
Теперь все бежим отмечать юбилей :)
Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Georgii Tormozov. What's new in .NET 10.