
Каждой осенью у разработчиков .NET наступает раннее Рождество: последние версии C# и .NET выходят из стадии превью и предоставляются в виде полностью поддерживаемых релизов с долгосрочной (LTS) или стандартной (STS) поддержкой.
Этой осенью вышел релиз с долгосрочной поддержкой (LTS), и, вероятно, в течение следующего года многие организации перейдут на него в качестве предпочтительной версии .NET.
Хотя есть множество полезных постов, охватывающих весь набор выпускаемых функций и улучшений, цель этой статьи — направить ваше внимание на те из них, которые больше всего впечатлили нашу команду, а именно:
- новые возможности языка C#, связанные с методами расширения и синтаксисом полей;
- улучшения Minimal API и документации OpenAPI в ASP.NET Core 10;
- новые функции и улучшения для Entity Framework Core;
- новая модель поддержки релизов без долгосрочной поддержки (non-LTS)
Итак, рассмотрим основные моменты.
Улучшения в языке C# 14
Хотя в C# 14 представлен ряд изменений, включая расширенную поддержку Span и ReadOnlySpan, новое нулевое условное присваивание (mayBeNull?.Name = "This is only set if mayBeNull is not null"), частичные конструкторы и некоторые другие усовершенствования, хочу выделить несколько изменений, связанных с работой со свойствами.
Ключевое слово field
В C# автоматические свойства (auto-properties) появились уже давно. Они автоматически создают резервные поля, что приводит к более лаконичному и сфокусированному синтаксису, повышающему производительность разработчика. С годами свойства и автоматические свойства продолжали развиваться и улучшаться, но все еще остаются случаи, когда может потребоваться явное объявление свойства.
Например, следующий код раньше требовал объявления поля вручную для проверки:
private string _firstName;
public string FirstName
{
get => _firstName;
set => _firstName = value ?? throw new InvalidOperationException("First name is required");
}
Теперь с ключевым словом field можно полностью отказаться от явного объявления поля и использовать field для ссылки на поле, которое .NET генерирует для нас:
public string FirstName
{
get => field;
set => field = value ?? throw new InvalidOperationException("First name is required");
}
Это минимальное изменение, но мне оно нравится, так как сокращает объем нефункционального кода в файле. Это, в свою очередь, помогает сохранить сфокусированность кода на поведении, а не на структуре или формальностях.
Свойства расширений и альтернативный синтаксис методов расширения
С появлением LINQ мы получили возможность создавать методы расширения, которые выглядят как новые методы, «присоединенные» к существующим классам, с помощью такого синтаксиса:
public static class StringExtensions
{
public static string DoOurCustomTransform(this string input)
{
return input.ToUpperInvariant().Trim();
}
}
Это позволяло вызывать метод в таких сценариях:
string prompt = Console.ReadLine();
string cleaned = prompt.DoOurCustomTransform();
В C# 14 такая функция по-прежнему доступна, но теперь для этого появился альтернативный синтаксис с использованием ключевого слова extension:
public static class StringExtensions
{
extension(string input)
{
public string DoOurCustomTransform()
{
return input.ToUpperInvariant().Trim();
}
}
}
Обратите внимание: параметр перемещён из метода в блок extension, а ключевое слово static больше не требуется.
Поскольку я преподаватель в прошлом, считаю, что этот синтаксис будет более интуитивно понятен для новичков. Однако главное преимущество этого синтаксиса — возможность объявлять члены расширения:
public static class StringExtensions
{
extension(string input)
{
public string DoOurCustomTransform()
{
return input.ToUpperInvariant().Trim();
}
public string CleanedString => input.DoOurCustomTransform();
}
}
В этом блоке добавлено свойство CleanedString, которое использует наше преобразование. Таким образом, можно писать код, вызывающий это свойство (геттер) для любой строки, используя следующий синтаксис:
string userInput = Console.ReadLine();
string cleaned = userInput.CleanedString;
Это довольно лаконично и удобно, а также расширяет возможности по добавлению новых API к коду и распределению кода по разным файлам, если такая организация для вас целесообразна.
Я ожидаю, что свойств расширений будет использоваться меньше, чем методов расширений, но я рад, что такая возможность теперь есть. Кроме того, считаю, что новый опциональный синтаксис более удобен для новичков и полезен для группировки связанных методов расширений и свойств расширений, поэтому, вероятно, я начну постепенно переходить на новый синтаксис при написании кода.
Усовершенствования, связанные с Minimal API
Изучая полный список изменений в ASP.NET Core 10, можно заметить, что там много всего, связанного с различными аспектами ASP.NET, включая Blazor, аутентификацию, SignalR и другие области. Однако больше всего меня привлекли возможности, связанные с управлением API через Minimal API и OpenAPI.
Когда появились Minimal API, они предоставили лаконичный способ определения простых конечных точек в обычном коде C# без необходимости в классах Controller. Эти конечные точки отлично подходили для простых демонстраций и микросервисов, которым не нужно предоставлять широкий спектр различных конечных точек, но им также не хватало некоторых полноценных функций, присущих традиционным контроллерам.
В ASP.NET Core 10 предусмотрен ряд улучшений для конечных точек Minimal API, включая:
- улучшенную обработку пустых строк в телах запросов;
- поддержку атрибутов валидации и
IValidatableObject; - предоставление детализации проблем с помощью
IProblemDetailsService; - поддержку событий, отправляемых сервером.
Также мы видим новые улучшения в виде возможности поддержки OpenAPI 3.1 и значительное количество новых способов предоставления документации API через конечные точки Minimal API, включая:
- более точную документацию типов с использованием OpenAPI 3.1;
- свойство
Descriptionдля аннотацийProducesResponseType; - поддержку XML-документации для Minimal API со ссылкой на именованный метод для обработки;
- поддержку внедрения зависимости
IOpenApiDocumentProviderдля более сложных сценариев; - различные другие улучшения документации, связанные с
AsParameters, сценариями JSON Patch и многое другое
Хотя ни одно из этих улучшений не выделяется само по себе, вместе они свидетельствуют о достижении более профессиональных результатов при использовании Minimal API в рабочих сценариях и о лучшей поддержке документации OpenAPI.
Функции Entity Framework Core 10
В EF Core 10 появилось много нового, как можно увидеть из обзора полного списка изменений, но выделю несколько ключевых моментов — работу с эмбеддингами, объединениями и сложными типами, которые расширяют возможности EF для обработки более профессиональных сценариев.
Во-первых, EF добавил поддержку эмбеддингов для Azure SQL и SQL Server посредством объявления столбца SqlVector<float>. Это позволяет использовать генератор эмбеддингов для вычисления векторных представлений текста элемента и хранения в этом столбце. Затем можно использовать функцию EF.Functions.VectorDistance для вычисления расстояния между сохранёнными эмбеддингами и поисковым запросом. Эта поддержка векторов становится всё более актуальной в мире с искусственным интеллектом, где эмбеддинги критически важны для обеспечения возможностей RAG, таких как те, что можно увидеть в чат-приложениях, использующих эту технологию.
Во-вторых, EF теперь поддерживает столбцы со сложными свойствами из JSON-данных. Это позволяет использовать строго типизированные определения классов C# для представления данных, которые хранятся в виде JSON в строках базы данных, не беспокоясь о сложностях сериализации и десериализации. Хотя я стараюсь избегать хранения JSON в базах данных, бывают случаи, когда это неизбежно или целесообразно для конкретного сценария. В таких случаях работать со сложными свойствами из JSON-данных очень удобно.
Кроме того, в EF представлен ряд других интересных функций, таких как фильтры именованных запросов, которые можно определять и при необходимости отключать. Это помогает сократить код, если вы часто используете одни и те же проверки (например, фильтрацию удаленных записей).
Очень рад был увидеть улучшения оптимизации запросов к SQL, которые увеличивают вероятность использования плана кэшированного запроса при применении оператора IN, так как я часто на него полагаюсь. Наконец, я очень впечатлен добавлением анализатора кода, который может помечать случаи, когда выполнение необработанного SQL-запроса делает возможным SQL-инъекции, поскольку эти атаки остаются распространенной уязвимостью для команд даже в конце 2025 года.
Изменения в стандартном сроке поддержке (STS)
Одним из самых значительных изменений в этом году в контексте LTS-релиза является изменение не самого релиза, а того, как долго будут поддерживаться STS-релизы.
.NET выпускает релизы с долгосрочной поддержкой (LTS) через год, а в промежуточные годы выходят релизы со стандартным сроком поддержки (STS). Это позволяет .NET продолжать поставлять новые функции и улучшения, но при этом дает организациям стабильность и долгосрочную поддержку, если они не могут часто обновляться.
Однако эти STS-релизы в основном использовались в непроизводственных сценариях, а их полные функции перенимались организациями в составе следующего LTS-релиза. Это происходило потому, что срок поддержки STS-релизов истекал раньше, чем у предыдущего LTS-релиза. Это означало, что у организаций, обновляющихся с LTS до следующего STS-релиза, дата окончания поддержки их приложения сдвигалась вперед при переходе на STS-релизы. Как следствие, большинство организаций оставались на LTS-релизах и использовали STS-релизы только в том случае, если им было критически важно получить что-то из нововведений.
С новым изменением STS-релизы теперь поддерживаются в течение 24 месяцев вместо 18. Это означает, что по сути для организации теперь нет никаких негативных последствий при переходе на STS-релиз, если она этого захочет, или при начале новых проектов с использованием этого STS-релиза вместо предпочтительного LTS-релиза.

Это изменение, будем надеяться, облегчит организациям внедрение STS-релизов и обеспечит одинаковую значимость LTS-релизов и будущих STS-релизов.
Заключительные мысли
Я лишь поверхностно коснулся того, что готовят C# 14 и .NET 10, но надеюсь, вы уловили общую тенденцию: мы видим не несколько громких нововведений, а платформу, которая продолжает оставаться актуальной и ежегодно улучшаться за счет новых удобств, поддержки отраслевых трендов, повышения производительности и последовательного совершенствования существующего функционала.
Я с энтузиазмом работаю с этими инструментами как в своих личных проектах, так и с клиентами, и я рад, что .NET продолжает идти по пути инноваций и развития.
Читайте также:
- Зачем использовать HttpClientFactory вместо HttpClient в .NET
- Встроенная поддержка контейнеров для .NET 7 — контейнеризация приложений .NET без Dockerfile
- C# — создаем клиент-серверный чат на TCP
Читайте нас в Telegram, VK и Дзен
Перевод статьи Matt Eland: Our Favorite New Features in .NET 10 and C# 14




