.NET 11 движется к финальному релизу: платформа уже находится на этапе Release Candidate, а общедоступная версия выйдет в ноябре 2026 года. Обновление распределено по всей экосистеме. Вместе с платформой поставляется C# 15 — с типами объединения, закрытыми иерархиями и пересмотренной моделью безопасности памяти. Windows Forms получает режим киоска и тонкую настройку отрисовки, EF Core 11 расширяет поддержку LINQ и полнотекстового поиска, ASP.NET Core приносит набор улучшений Blazor, а среда выполнения ускоряет асинхронность, диспетчеризацию интерфейсов и JIT-оптимизации.
В этом обзоре — самые значимые изменения .NET 11 по каждому направлению: что появилось, как этим пользоваться и на что обратить внимание при миграции.
Пятнадцатая версия языка делает систему типов выразительнее и даёт более точный контроль над небезопасным кодом.
Конструкция with(...), размещённая первым элементом выражения коллекции, передаёт аргументы прямо в конструктор базовой коллекции или фабричный метод. Так параметры вроде начальной ёмкости или компаратора задаются в самом синтаксисе инициализации:
string[] values = ["one", "two", "three"]; List<string> names = [with(capacity: values.Length * 2), ..values]; HashSet<string> set = [with(StringComparer.OrdinalIgnoreCase), "Hello", "HELLO", "hello"];
Значение, которое принадлежит ровно одному из нескольких типов-кейсов, описывается ключевым словом union:
public record class Cat(string Name); public record class Dog(string Name); public record class Bird(string Name); public union Pet(Cat, Dog, Bird);
Модификатор closed разрешает наследование только внутри объявляющей сборки: набор прямых наследников фиксируется на этапе компиляции.
public closed record class GateState; public record class Closed : GateState; public record class Open(float Percent) : GateState;
Блок extension теперь допускает индексаторы: приёмник индексируется так, будто индексатор объявлен в его типе. Поскольку это член экземпляра, блок обязан объявить именованный параметр приёмника.
Оба оператора научились принимать метку внешней конструкции. Это избавляет от логических флагов и goto при управлении потоком во вложенных циклах:
outer: for (int row = 0; row < grid.Height; row++) { for (int column = 0; column < grid.Width; column++) { if (grid[row, column].IsBlocked) continue outer; if (grid[row, column].IsGoal) break outer; } }
С версии C# 15 стартует рассчитанная на несколько релизов переработка модели безопасности памяти. Небезопасный контекст привязывается к операциям, которые реально обращаются к неуправляемой памяти, а не к самому наличию указателей. Член помечается как requires-unsafe, и ответственность за аудит переходит к вызывающему коду; факт применения правил фиксируется атрибутом MemorySafetyRulesAttribute. Появляется и ключевое слово safe — для членов и extern-полей в типах с явной или расширенной компоновкой.
В режиме preview ослаблены требования к указателям: объявление типа-указателя, взятие адреса через &, инструкция fixed, преобразование stackalloc в указатель и sizeof для неуправляемых типов больше не требуют unsafe. Разыменование (*p), доступ к члену (p->member), доступ по индексу (p[i]) и вызов через указатель по-прежнему требуют unsafe. Новое выражение unsafe(expression) ограничивает небезопасный контекст одним выражением — оно выручает в инициализаторах полей и конструкторов, а также в фильтрах catch.
Для включения нужны LangVersion=preview и AllowUnsafeBlocks; обновлённые правила активирует функция updated-memory-safety-rules.
Обновление отвечает на практические запросы разработчиков десктопных приложений.
KioskModeManager избавляет от собственной логики управления окнами: сохраняет состояние формы при входе в полноэкранный режим и выходе из него, блокирует отключение дисплея и сон, прячет курсор после бездействия и обрабатывает сочетания клавиш. Менеджер работает с ContainerControl, а значит — и с Form, и с UserControl.
Application.SystemVisualSettings даёт единый снимок оформления: акцентный цвет, высокую контрастность, видимость подсказок клавиатуры, анимацию, метрики границы фокуса и масштаб текста. Событие SystemVisualSettingsChanged позволяет обновлять только затронутый участок интерфейса, а свойство ChangedCategories сообщает, что именно изменилось. Элементы управления могут переопределять метод OnSystemVisualSettingsChanged.
Перечисление FormRevealMode передаёт приложению управление моментом появления формы при запуске. Это помогает предотвратить кратковременное появление окна без оформления. Режим Deferred включает отложенный показ, Classic сохраняет прежнее поведение.
Интерфейс ISupportSuspendPainting и метод ControlMutationExtensions.SuspendPainting объединяют пары BeginUpdate/EndUpdate и SuspendLayout/ResumeLayout в одну IDisposable-область, а свойство LayoutSuspendTraversal определяет, охватит ли пауза только целевой объект или ещё и потомков. Это гасит лавинообразные перерасчёты макета и мерцание — особенно во вложенных TableLayoutPanel с автоподбором размера. ComboBox, ListBox, ListView, RichTextBox и TreeView уже реализуют новый интерфейс.
Одиннадцатая версия EF Core углубляет работу с реляционными сценариями и поиском.
Оператор FullJoin сохраняет строки из обеих входных коллекций и транслируется в FULL JOIN:
var results = await context.Customers .FullJoin( context.Orders, c => c.Id, o => o.CustomerId, (c, o) => new { Customer = c, Order = o }) .ToListAsync();
Методы MaxByAsync и MinByAsync (и их синхронные аналоги) теперь транслируются в SQL. Это позволяет находить элемент по максимальному или минимальному значению селектора без загрузки всех записей:
var blogWithMostPosts = await context.Blogs.MaxByAsync(b => b.Posts.Count());
Функция проверяет наличие JSON-пути в документе; в SQL Server она транслируется в JSON_PATH_EXISTS (начиная с SQL Server 2022).
Полнотекстовые каталоги и индексы настраиваются прямо в модели, чтобы миграции создавали их автоматически. Векторный поиск SQL Server комбинируется с полнотекстовыми табличнозначными функциями в гибридный поиск: точные совпадения от полнотекста и семантически близкие результаты от векторов объединяются по ранжированию. Поддержка FullJoin делает шаблон полнее:
var results = await context.Articles .FreeTextTable<Article, int>(textualQuery, topN: k) .Join(context.Articles, fts => fts.Key, a => a.Id, (fts, a) => new { Article = a, fts.Rank }) .FullJoin( context.Articles.VectorSearch(a => a.Embedding, queryEmbedding, "cosine") .OrderBy(r => r.Distance) .Take(k) .WithApproximate(), fts => fts.Article.Id, vs => vs.Value.Id, (fts, vs) => new { Article = fts != null ? fts.Article : vs.Value, FullTextRank = fts == null ? null : (int?)fts.Rank, VectorDistance = vs == null ? null : (double?)vs.Distance }) .Select(x => new { x.Article, RrfScore = (x.FullTextRank == null ? 0.0 : 1.0 / (k + x.FullTextRank.Value)) + (x.VectorDistance == null ? 0.0 : 1.0 / (k + x.VectorDistance.Value)) }) .OrderByDescending(x => x.RrfScore) .Take(10) .Select(x => x.Article) .ToListAsync();
Трансляция GroupBy переработана: запросы умеют композировать навигации и соединения поверх результата «верхний элемент на группу», а агрегаты по ссылочным навигациям идут через соединения до группировки, а не через коррелированные подзапросы. Не-сущностные проекции — анонимные типы и DTO — корректно материализуются через соединения и группировки.
При разделённых запросах (AsSplitQuery()) EF Core перестал добавлять лишние соединения для ссылочных навигаций и больше не дублирует их ключи в ORDER BY: ключ ссылочной навигации и так определяется ключом родителя через внешний ключ. В бенчмарках упомянут прирост до 29% для разделённого запроса и до 22% за счёт удаления избыточного ORDER BY.
ASP.NET Core 11 продолжает развивать Blazor в сторону гибкости и производительности.
По умолчанию серверные значения CurrentCulture и CurrentUICulture сохраняются в состоянии компонента и применяются на клиенте до загрузки вспомогательных сборок. Поведение отключается через WebAssemblyComponentsOptions.UseCultureFromServer = false.
Данные HTTP-сессии на основе cookie стали доступны во время статического рендеринга — это полезно для корзины или прогресса многошаговых форм. В отличие от ITempData, значения не очищаются после чтения и живут всю сессию. Потребуются AddSession() и UseSession().
Встроенный компонент Blazor для условной отрисовки по среде размещения. Принимает параметры Include и Exclude, сопоставляет значения без учёта регистра; семантика совпадает с MVC-помощником EnvironmentTagHelper.
WASM-приложения поддерживают фоновые службы IHostedService в браузере — достигнут паритет с Blazor Server и открыты сценарии периодического обновления данных, обновлений в реальном времени и фоновой обработки.
Метод WithBrowserOptions задаёт параметры запуска клиента на C# вместо ручного вызова Blazor.start: уровень журналирования, переподключение Interactive Server, сохранение DOM при расширенной навигации, имя среды, культуру и переменные среды WebAssembly:
app.MapRazorComponents<App>()
.WithBrowserOptions(options =>
{
options.InteractiveServer.ReconnectionDialogId = "reconnect-dialog";
options.InteractiveServer.ReconnectionMaxRetries = 10;
options.InteractiveWebAssembly.EnvironmentVariables["OTEL_EXPORTER_OTLP_ENDPOINT"] = "https://localhost:4318";
options.StaticServer.PreserveDom = true;
options.InteractiveWebAssembly.ApplicationCulture = "en-ca";
options.LogLevel = LogLevel.Warning;
});
Пакет Microsoft.AspNetCore.Components.Server.AutoPause приостанавливает цепь (circuit), когда вкладка скрывается, освобождая память сервера и соединения SignalR. После настраиваемой задержки (по умолчанию — две минуты) цепь встаёт на паузу; если пользователь вернулся раньше, пауза не сработает.
Компонент CacheView кэширует результат рендеринга поддерева Razor во время статического SSR. При попадании в кэш разметка воспроизводится без создания экземпляров и запуска жизненного цикла дочерних компонентов — это удобно для дорогих, преимущественно статических участков:
<CacheView VaryByQuery="category" ExpiresAfter="TimeSpan.FromMinutes(5)"> <ProductList Category="@Category" /> </CacheView>
Набор компонентов для агентских интерфейсов: трансляция хода работы в реальном времени, визуализация рассуждений и прогресса агента, запрос подтверждения перед вызовом инструментов, мультимодальный ввод и синхронизация состояния между приложением и агентом. Компоненты экспериментальные, поэтому API может меняться до стабилизации.
Среда выполнения ускоряет асинхронность и диспетчеризацию интерфейсов, а CoreCLR в WebAssembly приближается к практическому применению в Blazor.
Механизм Runtime Async перестал быть экспериментальным: среда выполнения собрана с его поддержкой по умолчанию. Дополнительно JIT научился компилировать специализированные версии синхронных методов, возвращающих Task, что заметно снижает накладные расходы на асинхронные вызовы.
Диспетчеризация интерфейсов в NativeAOT стала быстрее благодаря общему вспомогательному механизму. Это уменьшило объём двоичного кода в местах вызова и повысило пропускную способность в сценариях с интенсивным использованием интерфейсов.
Работа над JIT продолжается по нескольким направлениям: устранение проверок границ, девиртуализация и свёртывание констант. На x64 метод Math.BigMul теперь компилируется в единственную инструкцию MUL.
CoreCLR в WebAssembly достиг важной вехи: он проходит полный набор библиотечных тестов. Это приближает момент, когда Blazor WebAssembly сможет работать на CoreCLR.
.NET 11 — релиз, в котором изменения распределены равномерно по всей экосистеме, а не сосредоточены в одном языке или фреймворке. C# 15 задаёт вектор на более строгую типизацию и явное управление небезопасным кодом; Windows Forms отвечает на практические запросы разработчиков десктопных приложений; EF Core 11 углубляет работу с реляционными и гибридными сценариями поиска; ASP.NET Core развивает Blazor в сторону гибкости и производительности; среда выполнения ускоряет асинхронность, диспетчеризацию интерфейсов и JIT, а CoreCLR в WebAssembly приближается к практическому применению в Blazor.
Часть возможностей — прежде всего связанных с безопасностью памяти — заявлена как многорелизная инициатива и будет раскрываться постепенно. Командам, планирующим миграцию, стоит заранее оценить, какие нововведения затрагивают кодовую базу напрямую, а какие можно принять позже. Поскольку платформа уже на этапе Release Candidate, самое время протестировать свои проекты на релиз-кандидате .NET 11: так к ноябрьскому релизу вы встретите обновление во всеоружии.