Net 6 что нового
Перейти к содержимому

Net 6 что нового

  • автор:

Net 6 что нового

8 ноября компания Microsoft выпустила финальную версию фреймворка .NET 6.0 с C# 10 и F# 6, а также Visual Studio 2022. .NET 6 ознаменовался значительным увеличением поизводительности по сравнению с предыдущими версиями фреймворка и большим количеством нового функционала. Кроме того, это LTS-версия, которая будет поддерживаться в течении следующих трех лет.

.NET 6.0 представляет унифицированную платформу, которая позволяет создавать десктопные и мобильные и веб-приложения, а также приложения для IoT и облака.

.NET 6 и C# 10

Некоторые основные изменения в этой версии:

Благодаря внедрению Dynamic Profile-guided Optimization (PGO) удалось повысить производительность. Dynamic PGO базируется на механизме уровневой компиляции Tiered Compilation, которая позволяет произвести на старте быструю базовую компиляцию методов («уровень 0»), а когда метод потребует, произвести его перекомпиляцию с рядом оптимизаций («уровень 1»)

Полностью переписан API для работы с файлами, в частности, класс FileStream . Это привело к значительному увеличению производительности: увеличилась скорость выполнения и значительно уменьшилось выделение памяти.

Более быстрая проверка интерфейсов и использование их в преобразованиях типов. По данным Microsoft, производительность увеличилась на 16% – 38%.

Добавлены генераторы кода для API System.Text.Json , что опять же повысило производительность и уменьшило выделение памяти. Кроме того, JsonSerializer поддерживает IAsyncEnumerable :

Релиз .NET 6.0 также ознаменовался выходом новой версии языка C# — C# 10. Некоторые из его новвоведений:

Глобальные пространства имен. Достаточно подключить пространство имен в один файл кода с помощью директивы global using , и оно будет доступно во всех файлах проекта.

Пространства имен уровня файла. Так, вместо

Теперь можно создавать объекты record в виде структур (раньше все record представляли классы)

Также вместе с фреймворком вышла новая версия F# — F# 6

.NET 6.0 позволяет использовать Hot Reload для динамческой перезагрузки приложения при изменении кода

Это первая версия фреймворка, которая поддерживает архитектуру Apple Silicon (Arm64) и значительно усовершенствована для работы Windows Arm64.

Некоторые изменения затронули Windows Forms, в частности, возможность установки шрифта для приложения, минимизация кода и ряд других

Добавлен ряд новых API:

Новые API в System.Math , например, SinCos для одновременного вычисления синуса и косинуса и ряд других

API для сжатия для WebSocket

Поддержка прокси для Socks4, Socks4a и Socks5

В Microsoft.Extensions.Hosting добавлено ConfigureHostOptions API

В Microsoft.Extensions.DependencyInjection добавлено CreateAsyncScope API для использования сервисами IAsyncDisposable

В System.Linq методы Enumerable.ElementAt и Enumerable.Take имеют поддержку Index и Range

Также добавлены новые методы DistinctBy/UnionBy/IntersectBy/ExceptBy/MaxBy/MinBy

А в методах FirstOrDefault/LastOrDefault/SingleOrDefault теперь можно установить значение по умолчанию:

Очередь на основе приоритетов — PriorityQueue

Новые структуры для представления дат и времени: DateOnly и TimeOnly

Отмечается, что .NET 6 поддерживается в Visual Studio 2022 и Visual Studio 2022 for Mac. Однако НЕ поддерживается в Visual Studio 2019 и Visual Studio for Mac 8. Кроме того, .NET 6 поддерживается в Visual Studio Code с помощью расширения для C#.

What’s new in .NET 6 and C# 10. Everything you wanted to know.

This blog post is a compilation of the latest and greatest additions from the .NET 6 release. Also, I’ve created a coding story that will help you to learn new improvements.

minimal-api-banner

Please check the coding story ��:

Source code ��:

⚠�� Please note that the content below consists of excerpts from the actual coding story. Please check the coding story if you haven’t already.

What’s New in Microsoft .NET 6?

Dreamix

.NET 6 is now released, bringing a new long-term stable version of .NET Core to the market. Now, the newer .NET 6 replaces .NET 5, largely viewed as a “skip version” by most developers, with minimal usage in contrast to dot NET Core 3.1.

Microsoft’s plan to provide a uniform platform for developing applications for any device and operating system gains traction with the release of .NET 6. With this ambition in mind, .NET 6 introduces a slew of new capabilities aimed at making development easier and improving performance.

Unified and Extended Platform

The objective of a single platform for the .NET world began to take shape with the release of .NET 5. That version was planned to be the unifying successor to all of the various .NET flavours, e.g. Framework, Standard, Core, Mono, and so on. .NET 5 also aimed to be a development platform to create any sort of application for any platform. However, at the time, an important piece of the puzzle was missing: a unified framework for designing cross-platform user interfaces. Thanks to .NET MAUI, or the .NET Multi-platform App UI framework, the riddle has been solved. Despite the fact that .NET MAUI is still in preview at the time of writing (February 2022), it is ready to be included in the .NET 6 release.

Nowadays with .NET 6, you can develop any application and launch it practically anywhere: on a PC, a mobile device, the web, or the cloud.

The new .NET MAUI framework, in particular, allows you to design desktop and mobile native user interfaces from the same codebase.

Simplified Development with .NET 6

The main goal of the .NET 6 release is to provide features to developers that simplify their work.

Below, I am going to explain most of them, however, the most exciting contexts are: new C# features, Hot Reload support, and minimal web APIs.

The new C#10 Features:

So .Net 6 comes with C#10 which brings you significant new features that will help you and make your code prettier, faster and a bit more expressive.

Let’s take a quick look at some of them:

We’ll start with global and implicit using because no C# developer avoids writing using directives in hundreds of files. This is undoubtedly infuriating, but thanks to .NET 6 and C# 10, we have an easy remedy. Consider the System namespace as an example; it’s a really typical scenario, and you’re probably forgetting about it all the time.

As seen in the example, you can declare namespace using a global keyword before utilising it.

global using System;

When you mark System; as global, you no longer have to include the using System; declaration in all files. This namespace will be available throughout the project.

Another method to get that feature (which I believe is a good practice) is to create a file called GlobalUsing.cs and specify your namespace there.

There is a third way to use implicit using by configuring the <ImplicitUsings> element in your .csproj file.

As a result, we enable the compiler to decide and automatically add global using directives based on the type of your project.

File Scoped Namespace Syntax

The majority of the code in a normal C# source file is indented because it is contained within a namespace declaration. It’s been like that for so long that we no longer notice it, but it’s a waste of horizontal space! C# 10 solves the issue by allowing you to specify a namespace for the entire file without the use of braces. By deleting a nesting level, you tidy up the code. This new syntax, however, does not allow for the creation of nested namespaces.

Lambda Improvements

Currently, you must declare the target type of the expression, as demonstrated in the example:

Func<int, bool> odd = n => (n % 2) == 1;

You may write the above code in C#10 more “naturally,” and the compiler will try to guess the type of the lambda expression for us.

var odd = (int n) => (n % 2) == 1;

Record Struct

The initial introduction of the record keyword for classes was in C#9. This keyword is applied to reference types that include built-in capabilities for data encapsulation. Records are primarily meant to enable immutable data models.

public record UserProfile(string AccountName, string Email);

C#10 takes records a step further. Currently, you can use structure types to build data-centric types that have the same values but little or no behaviour. In C# 10 and later, record struct types can be defined as follows:

public readonly record struct UserProfile(string AccountName,string Email);

Hot Reload

The Hot Reload update allows you to alter your app’s managed source code while it is running, removing the need to manually pause or hit breakpoints. Simply make a change while the app is running and then click the apply code changes button. It includes the most recent release of Visual Studio 2019.

Minimal Web APIs

The minimal web APIs are a fantastic new feature introduced by .NET 6 alongside new top-level statements. With just a few lines of code, you can have a fully functional web API application. Please, take a look at the example below.

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

If you’ve worked with web APIs in ASP.NET Core previously, you’re surely aware that there are numerous configurations to make before executing your web API. On the other hand, with the minimal API, everything is straightforward; there is a single file that does not require any further configurations.

This is a wonderful example of simplicity!

.NET 6 Performance Improvements

The .NET team has been working intensively in recent years to enhance the overall platform speed in terms of compilation and execution time, as well as memory use. Developers can see these enhancements in various places of the framework with .NET 6. For example, the endeavour to increase inner-loop efficiency in .NET project development has yielded fantastic results, as shown in the diagram below:

The IO subsystem is another area with performance improvements. For instance, the FileStream class was almost totally redesigned with outstanding results even when compared to.NET 5, as summarised in the table below for the writing operation:

For further information, see a thorough report on speed enhancements in.NET 6 with accompanying benchmarks.

Final Words

Since .NET Core 1.0, .NET 6 has been the most foundational release. It provides support for significant new hardware platforms, and broader use of source generation. Despite discussing many key new features, this article couldn’t touch on yet another performance gain, and dozens of other enhancements. .NET 6 is a reminder that Microsoft is investing in .NET for the long term, across client and cloud platforms. If you’re building cloud or client apps or both, .NET offers a multitude of useful options. Looking ahead, it looks that what follows next will be even more hopeful. It’s a fantastic time to be a .NET developer, as it always has been.

If you’re looking for a technological partner to assist you with custom .NET development, you’ve come to the right place. We at Dreamix have gained significant industry and hands-on experience and would be happy to help you with your next .NET project.

P.S This article was originally written by Kosta Georgiev, Middle Software Engineer 2 and published on our Dreamix blog in February 2022.

Что нового в .NET 6?

На момент написания этих строк вышло уже семь превью-версий .NET 6. Дальше — только релиз-кандидаты. Все основные фичи уже добавлены во фреймворк, идёт отладка, тестирование и оптимизация. Ожидать чего-то кардинально нового в RC-версиях, пожалуй, уже не стоит. Пришла пора рассмотреть .NET 6 поближе.

Пресс-релиз для каждой версии содержит огромное количество восхвалений и убеждений в том, что теперь-то всем станет ещё лучше, и как мы вообще жили раньше — уму непостижимо. Где-то авторы не врут, где-то не договаривают, где-то преувеличивают. Пришлось тщательно прочитать все семь пресс-релизов, изучить массу смежных материалов и просмотреть огромное количество тикетов на Гитхабе. Всё для того, чтобы понять, чем они там занимаются и что выкатывают нам посмотреть.

Поговорим об этом.

Производительность

Разработчики .NET всегда делали упор на производительность. С одной стороны, в язык и фреймворк постоянно добавляются новый функционал — ref struct , stackalloc , System.Span<T> и всё такое прочее. С другой стороны, с каждой новой версией .NET добавляются новые оптимизации — многопроходная (tiered) компиляция, компиляция в нативный код и, разумеется, огромное количество оптимизаций, которые делает JIT-компилятор. Грамотное использование этих средств даёт свой эффект, который хорошо видно в реальных боевых условиях на графиках производительности.

В NET 6 представлены три инструмента, которые дают ещё большие возможности для повышения эффективности. Причём, не только для самих приложений, работающих в продакшне, но и для разработчиков. Речь идёт о прокачаной предварительной компиляции (через утилиту Crossgen2), оптимизации на основе профилирования (PGO) и горячей перезагрузке приложений во время отладки.

Предварительная компиляция

Как известно, преимущества JIT-компиляции имеют свою цену. В частности, повышенное время «прогрева» приложения во время старта, поскольку JIT-компилятору требуется перемолоть разом слишком много IL-кода. Эту проблему уже пытались решить компиляцией приложений сразу в нативный код, такая технология уже есть и называется Ready To Run. Но в новой версии фреймворка её значительно переработали.

Старая технология предварительной компиляции была слишком примитивна и позволяла только генерировать нативный код для той платформы, на которой была запущена старая утилита crossgen. Разработчики полностью переписали её с нуля на управляемом коде и назвали Crossgen2. Теперь она предоставляет новые возможности: авторы делают упор на оптимизации, а также использование различных стратегий компиляции для разных платформ (Windows/Linux/macOS/x64/Arm). Всё это достигается новой архитектурой утилиты.

Вкратце это работает так: Crossgen2 разбирает IL-код, составляя некий граф приложения. Затем он запускет внутри себя JIT-компилятор для необходимой платформы, а этот компилятор, анализируя составленный граф, уже создаёт нативный код, применяя при необходимости различные оптимизации. Другими словами, утилита Crossgen2 может быть запущена на платформе x64, но она сгенерирует нативный и даже оптимизтированный код для Arm64. И, разумеется, наоборот это тоже работает.

В настоящий момент код .NET SDK скомпилирован уже с помощью Crossgen2, а старая утилита crossgen отправлена на пенсию.

Оптимизация на основе профилирования

Ещё одна новая старая фишка в .NET 6 — это Profile-Guided Optimization (PGO). Ни для кого не секрет, что обычно в приложении никогда не исполняется вообще весь написанный код. Какой-то код работает чаще других, какой-то вызывается в крайне редких случаях, а какой-то вообще никогда. Но компилятор обычно ничего об этом не знает, а лучше бы знал. Чтобы научить этому компилятор используется PGO-оптимизация. Её смысл заключается в том, что приложение просто прогоняется на разных стандартных кейсах, а заодно профилируется. Итоги профилирования анализируются компилятором, и он начинает распознавать самые часто используемые места кода, уделяя им особое внимание и оптимизируя их более тщательно.

Такое обучение компилятора похоже на обучение нейронной сети. К слову, в некоторых других распространённых языках программирования технология PGO реализована уже давно, но в .NET до этого добрались только сейчас. Эта тема довольно замороченная, и ребята занялись ей очень серьёзно, реализовав несколько различных подходов к компиляции итогового нативного кода.

Один из подходов — разделение на часто и редко используемый код (hot-cold splitting). Те части кода, которые используются наиболее часто (hot code), группируются и помещаются рядом в итоговом бинарнике. Если сильно повезёт, то такой сгруппированный код полностью поместится в кеш процессора, и вызовы различных часто используемых методов будут практически бесплатными и очень быстрыми. Напротив, некий крайне редко используемый код (very cold code) может вообще не быть скомпилирован в нативный. Например, else -ветки, в которых просто выбрасывается исключение. Такой код остаётся в виде IL-кода и будет скомпилирован в нативный уже после запуска приложения и только в том случае, если это будет необходимо. Такое разделение позволяет не только добиться более высокой производительности при старте, но и генерировать бинарники меньшего размера.

Другой подход — динамическая PGO. То есть, все этапы предварительного обучения JIT-компилятора пропускаются, а вместо этого он внимательно смотрит на то, как приложение работает в реальной среде и при необходимости заново компилирует какой-либо участок кода в более оптимальный. Если вы помните, то подобная технология уже существует — это многопроходная (tiered) компиляция (упоминается в начале статьи). Но разработчики JIT-компилятора просто серьёзно её прокачали.

Третий подход — сочетание динамического и статического профилирования для генерации нативного кода. Обе эти техники реализуются одновременно, и итоговый бинарник может частично содержать нативный код для быстрого запуска, который затем может быть оптимизирован ещё больше. В качестве примера приводится ситуация, когда некий интерфейс в программе реализован только в одном классе. В этом случае JIT-компилятор может девиртуализировать вызовы методов интерфейса и сгенерировать код для прямых вызовов методов класса, а также допустить встраивание этих методов прямо в код (inlining), если это будет необходимо.

Техника PGO работает в тесной связке с утилитой Crossgen2 и позволяет генерировать оптимизированный нативный код, а также экономить на размере итоговых бинарников. Но нужно отдавать себе отчёт в том, что статическая PGO — это довольно сложно для обычного разработчика. Ведь ему придётся заниматься многократным профилированием своего кода, результаты которого (а это очень много информации) нужно будет специальным образом подавать на вход при компиляции через Crossgen2. И хорошо, если результаты профилирования в тестовой среде будут пригодны и для продуктивной среды — тогда итоговый профит получить можно. Скажем, приложение будет гораздо быстрее запускаться и прогреваться. Это важный фактор, но надо помнить, что цена такой оптимизации — ресурсы, затраченные на предварительное профилирование, которое должно быть проведено очень аккуратно. Если при прогоне приложения на тестовой среде вы сделаете упор на редкие кейсы (например, тестировщики будут прогонять только негативные сценарии, пытаясь всё сломать), то данные профилирования у вас будут сильно отличаться от боевых. А значит, в итоговом бинарнике у вас предкомпилированным и оптимизированным может оказаться вообще не тот код.

Но, слава богу, есть ещё динамическая PGO, которая не требует предварительных ресурсозатрат и может повышать производительность вашего приложения прямо на лету, причём, делать это более эффективно, чем уже реализовано в существующей многопроходной компиляции. Правда, как вы понимаете, каждый новый рестарт приложения — это всякий раз оптимизация заново.

В общем, выбор у вас есть. Делайте его по ситуации.

Горячая перезагрузка приложений

Эта новая возможность действительно впечатляет. Любому разработчику хочется при отладке быстро пофиксить какой-то мелкий кусок кода без последующей перезагрузки приложения и прохождения заново всего пути к месту отладки. Такая возможность была и раньше, но в очень сильно упрощённом варианте и только в мощной IDE, вроде Visual Studio. Теперь же её прокачали настолько, что она реально позволит сэкономить уйму времени, избавившись от постоянных действий остановка-правка-ребилд-деплой-запуск-достижение точки отладки, причём, в любой IDE, даже в VS Code.

Это работает ещё интереснее, чем вы можете себе представить. Не нужно устанавливать брейкпойнт или ставить приложение на паузу во время отладки. Достаточно просто внести изменения в код и применить их прямо к работающему приложению. В последних билдах Visual Studio это поддерживается легко и просто:

Но даже если вы пользуетесь не студией, а VS Code, то вы не будете ущемлены. Вам нужно просто запустить ваш проект с помощью новой команды dotnet watch . После этого любые изменения в исходных файлах будут автоматически обнаружены, скомпилированы и подгружены в работающее приложение без каких-либо телодвижений с вашей стороны. Вы увидите изменения без его перезагрузки. Проще некуда, и это работает.

В случаях посерьёзнее (например, при отладке приложений ASP.NET) вам придётся добавить настройку в launchSettings.json , разрешающую горячую перезагрузку, что вряд ли станет большой проблемой.

Ложкой дёгтя во всём этом является тот факт, что не всякое изменение можно применить с помощью горячей перезагрузки. Полный список тех действий, которые можно и нельзя перезагрузить по-горячему, можно увидеть здесь.

Ах, да: в F# горячая перезагрузка не поддерживается в принципе. Может, когда-нибудь позже. Просто попросите разработчиков об этом.

Более подробно о горячей перезагрузке написано в переводе на Хабре.

Прочие производительные плюшки

Кроме упомянутых трёх очень важных нововведений в обычном цикле разработки удалось найти массу других мест для оптимизации, ускорив тем самым процесс билда и запуска приложений: ликвидировали причины оверхедов, оптимизировали MSBuild, перевели Razor-компилятор на Roslyn source generator и даже позаботились о том, чтобы пореже трогать файлы и зря беспокоить антивирусное ПО.

В общем, производительность труда разработчиков должна вырасти. По крайней мере, на это намекает диаграмма снижения времени билдов.

Поддержка ОC и платформ

.NET 6 будет поддерживать ещё больше операционок и платформ. Полный список доступен по этой ссылке. Большое внимание уделяется платформе Arm64 в целом: улучшена поддержка Windows Arm64 и добавлена поддержка Arm64-чипов Apple. Что касается последних, то, как известно, эти чипы умеют работать как в нативном режиме, так и в режиме эмуляции x64. .NET 6 будет поддерживать оба режима и будет уметь создавать как обычный x64, так и нативный для Arm64 код. Для разработки под macOS теперь будут два типа проектов: net6.0-macos для x64 и net6.0-maccatalyst для Arm64 архитектур.

Полный список новых Target Framework’ов теперь выглядит так:

  • net6.0
  • net6.0-android
  • net6.0-ios
  • net6.0-maccatalyst
  • net6.0-macos
  • net6.0-tvos
  • net6.0-windows

Однако, с программированием для Apple-устройств есть один нюанс: существует требование, которые предъявляется к приложениям, публикуемым в App Store. Если разработчик приложения хочет, чтобы приложение запускалось как на x64, так и на Arm64 архитектурах, то оно должно быть скомпилировано как Universal Binaries. Вот с этим требованием пока всё плохо: оно просто не поддерживается в .NET 6. В следующей версии .NET 7 разработчики посмотрят, что можно сделать. Впрочем, это не самое критичное требование, пока можно прожить и без него.

В общем, теперь можно брать новые Макбуки.

Также .NET 6 теперь существует для нескольких новых Linux-дистрибутивов: Alpine 3.13, Debian 11 и Ubuntu 20.04 — соответствующие docker-образы создаются с первого превью .NET 6.

Унификация, поглощение Xamarin, «optional workloads» и MAUI

Ещё пару лет назад разработчики .NET объявили, что собираются объединить в одном .NET-флаконе разработку для всего сразу. Ну, то есть, ничего не будет, а будет одно сплошное телевидение один фреймворк для всего, что только есть на свете — и для мобильной, и для серверной, и для веб-разработки, и для IoT, и для… не знаю, что там ещё появится в будущем. И они назвали это .NET 5, перескочив, во-первых, через версию, чтобы не было путаницы с классическим .NET Framework 4.x, а во-вторых, объединив классический фреймворк с Core, к чему стремились с самого начала, просто осторожно шли окольными путями.

В качестве профита от такого объединения упоминались две ключевые фишки:

  • вы пишете на одном языке с использованием одного API;
  • вы не используете то, что вам не надо: новый фреймворк достаточно раздробленный, и вам не нужно устанавливать кучу ненужных библиотек.

Люди, знающие .NET, когда он ещё пешком под стол ходил, в этом месте начинали припоминать, что примерно такие же обещания раздавались налево и направо двадцать лет назад (а потом повторялись с появлением Silverlight и UWP). Классический фреймворк, вроде как, преследовал эти же самые цели, но только был неделимым, как атом, монолитом, заточенным под одну ОС. Однако, мир менялся быстрее и не в ту сторону. Но в MS вовремя опомнились и умудрились запрыгнуть в уходящий поезд, выпустив первую версию Core, да ещё и выведя разработку в Open Source.

Сейчас .NET далеко не в последнем вагоне этого поезда, и, похоже, тотальная унификация действительно не за горами.

Так вот. Пятую версию .NET выпустили, но унификация продолжается: добрались до Xamarin и поглотили его подружили его с .NET 6. Речь идёт, конечно же, о разработке под Android, iOS и macOS. Вообще, вы теперь и без Xamarin имеете возможность набрать команду dotnet new android и начать разрабатывать под Андроид. А запускать разработанное вы будете командой dotnet run . Но я попробовал — это не работает. Такого шаблона проекта даже нет в последней превью-версии .NET 6. Это потому, что соответствующие библиотеки для разработки под Андроид (а также iOS и macOS) — ну, то есть, то, что раньше было частью Xamarin — не являются частью стандартного .NET SDK. Их нужно скачивать отдельно. В первую очередь, это объясняется тем, что не хочется снова создать огромный монолит. В общем, всё постороннее, что пришло вместе с Xamarin, вынесено в «Optional SDK Workloads» — некие дополнительные части фреймворка, не входящие в стандартный SDK. Иначе размер SDK станет неприличным, а сам он начнёт противоречить одной из заявленных целей: не устанавливать кучу ненужного.

Вот этот вот новый «Optional SDK Workloads» теперь является частью .NET 6 и будет продолжать развиваться в .NET 7. Таким образом происходит слияние Xamarin с .NET. Но Xamarin в данном случае не только что-то отдаёт, но и получает взамен: разработка теперь будет вестись с использованием единой BCL, в едином стиле и с едиными подходами, а также можно будет использовать единую систему всех .NET-утилит, начиная с уже упомянутой dotnet new android . Разумеется, делается акцент и на сокращённом времени билда, уменьшении размеров итогового приложения, а таже улучшенной производительности.

Это ещё не всё, что происходит с Xamarin. Анонсировали новый .NET Multiplatform App UI (MAUI) — «эволюция» Xamarin Forms. С этого момента, думаю, про название «Xamarin Forms» можно уже начать забывать. Отныне вся кроссплатформенная UI-разработка будет называться MAUI. Разумеется, по своей сути MAUI — это мультиплатформенная абстракция над различными UI, родными для каждой конкретной платформы. На MAUI можно разработать интерфейсы, которые будут работать и на Blazor, и на мобильных платформах и даже в десктопных приложениях.

Также, скорее всего, можно начать забывать и про Mono, и про сам Xamarin. В шестой версии .NET они пока ещё живы как самостоятельные продукты, но есть подозрение, что седьмая поглотит их окончательно.

А пока разработчики на Xamarin получают возможность полноценно использовать родной .NET 6.0 SDK для кроссплатформенной мобильной разработки.

Как же теперь с этим всем работать, если не получается выполнить команду dotnet new android ? Ну, утилиту dotnet , вообще-то, доработали: для работы с «optional SDK workloads» теперь есть команда dotnet workload . Интересно, что она пока не выводится как доступная при вызове dotnet —help , но пользоваться уже можно:

Никто не мешает вам уже сейчас загрузить нужный дополнительный SDK и попробовать написать небольшой «Hello World» для вашей мобилки. И даже, наверное, без установки Xamarin. Самое приятное: обещают, что можно будет работать с этим в VS Code, не надо будет ставить могучую и неповоротливую полноценную Студию. Желающие могут это сделать прямо сейчас, скачав готовые примеры из репозитория.

Ждём в .NET-разработку притока мобильщиков?

Blazor на десктопе

Оказывается, Blazor стал достаточно популярным (по заверениям разработчиков .NET), причём, настолько, что было решено сделать десктоп-версию Blazor-приложений. Модель разработки это позволяет.

В общем, теперь вы можете написать Blazor-приложение, которое запустится не только в браузере как WebAssembly, но и на Windows и macOS как нативное десктопное.

Улучшения в System.Text.Json

Вот и добрались до изменений в SDK. А их достаточно много, очень сложно пройти мимо. Начнём с System.Text.Json — эту библиотеку очень сильно прокачали.

Игнор цикличных ссылок

В сериализатор добавили опцию игнорирования цикличных ссылок.

Обратите внимание на то, что сериализатор заменяет ссылку на null , а не игнорирует свойство полностью.

Поддержка IAsyncEnumerable<T>

Сериализатор System.Text.Json теперь поддерживает IAsyncEnumerable<T> -объекты. При сериализации он их превращает в массивы:

Для десериализации JSON-документов, которые представляют собой просто массив на корневом уровне, добавили новый удобный метод JsonSerializer.DeserializeAsyncEnumerable :

JSON DOM

Самое интересное нововведение — это возможность работать с JSON-документом как с DOM. Эта особенность довольно полезна, поскольку часто просто не хочется плодить POCO-объекты для простых операций. Вот пример того, как это теперь работает:

До сих пор в подобных случаях нужно было пользоваться классами Utf8JsonWriter / Utf8JsonReader , но DOM-подход тоже неплох.

Поддержка source generators для сериализации

В плане перерасхода ресурсов от DOM-модели не сильно отстаёт обычная сериализация и десериализация. Она основана на рефлексии, а это заведомо медленно. Поэтому там, где реально нужна производительность, всегда лучше было работать с . Writer и . Reader классами (это правило касается не только работы с JSON, но также и с XML). Такая работа занимает больше времени, но окупается максимальной производительностью на продакшне.

Однако разработчики .NET 6 и тут придумали обходной манёвр для облегчения жизни разработчиков: source generators. Эту новую технологию завезли в System.Text.Json , и она решает все основные проблемы, связанные с низкой производительностью обычных сериализаторов: уменьшает время старта приложения и количество используемой памяти, увеличивает скорость работы, не использует рефлексию. Что же тогда используется взамен, если не рефлексия? Именно тот самый класс Utf8JsonWriter , через который и происходит работа с JSON.

Выглядит такая техника точно так же, как и при любой другой работе с source generators. Сначала вы создаёте тип для сериализации/десериализации:

Как видите, он слишком простой, но для иллюстрации работы этого достаточно. Затем вы создаёте partial -класс и сопровождаете его соответствующим атрибутом:

После этого на этапе компиляции ваш частичный класс будет расширен несколькими методами и свойствами:

Через одно из этих свойств — JsonMessage вы получите доступ к сгенерированному сериализатору, работа с которым будет выглядеть как-то так:

Стандартный сериализатор также прокачан и может принимать на вход сгенерированный с помощью source generator код:

Второй способ будет работать чуть-чуть медленнее, но всё равно такая сериализация через генерацию кода будет работать куда быстрее, чем старая сериализация через рефлексию.

Разумеется, сериализация через генерацию кода поддерживает не только примитивные типы, но и объекты (в том числе, вложенные), коллекции и всё остальное.

https://amdy.su/wp-admin/options-general.php?page=ad-inserter.php#tab-8

К сожалению, десериализация через source generators пока не поддерживается. Единственное, что разработчики добавили, — это поддержку в стандартном десериализаторе сгенерированных типов:

Но даже в этом случае никаких Utf8JsonReader не будет. Только рефлексия, только хардкор.

Поддержка нотификаций при (де)сериализации

В специальный неймспейс System.Text.Json.Serialization добавили четыре интерфейса: IJsonOnDeserialized , IJsonOnDeserializing , IJsonOnSerialized и IJsonOnSerializing . Они нужны для вызова методов в процессе (де)сериализации. Как правило, в целях валидации:

Но вы можете придумать и какое-нибудь своё применение.

Порядок следования полей при сериализации

С помощью специального атрибута JsonPropertyOrder теперь можно управлять порядком, в котором сериализованные поля будут помещаться в итоговый JSON:

Ранее порядок, в котором поля попадали в итоговый JSON, был, скажем так, не совсем предсказуем.

Utf8JsonWriter : возможность вывести напрямую JSON-текст

В класс System.Text.Json.Utf8JsonWriter добавили метод WtiteRawValue , и теперь в JSON можно писать raw-текст:

В данном примере решается одна известная проблема сериализатора: он не пишет дробную часть вещественного числа, если она равна нулю. То есть, число 1.1 будет выведено с вещественной частью, а число 1.0 будет выведено как целое. В ряде случаев такое поведение неприемлемо, и теперь вы можете управлять им с помощью нового метода.

Десериализация из Stream

Оказывается, раньше не было возможности десериализовать поток. Теперь есть:

Новая коллекция PriorityQueue

Она представляет собой ту же самую очередь, но каждый элемент которой теперь имеет приоритет. При помещении элемента в очередь вы этот приоритет указываете. При извлечении элемента из очереди сначала извлекаются те элементы, приоритет которых имеет минимальное значение:

Как видно из примера, в случае равенства приоритетов порядок извлечения элементов не гарантирован.

Очень интересная коллекция, вполне подойдёт для некоторых случаев.

Source Generator для ILogger

Новая фича .NET 5 — Source Generators — добралась до логгера. Теперь можно писать меньше кода для логгинга, потому что недостающий код будет создан автоматически. Вам достаточно лишь пометить специальные методы специальным атрибутом LoggerMessageAttribute , и весь недостающий код будет скомпилирован за вас, причём, он будет более оптимальным и производительным.

Только не забудьте пометить и метод, и содержащий его класс ключевым словом partial .

Детали уже можно почитать в документации.

Улучшения в System.Linq

В LinqExtensions добавили массу полезных методов и фич. Например, поддержку диапазонов и индексов. Теперь можно попросить вернуть второй с конца элемент коллекции:

А в метод Take() добавили классную перегрузку:

Новый метод TryGetNonEnumeratedCount() сильно помогает в случаях, когда надо узнать количество элементов коллекции без её перебора:

Если source — это просто переменная типа IEnumerable , то попытка получить количество элементов коллекции может вызывать полный перебор коллекции раньше времени. А с помощью TryGetNonEnumeratedCount() можно и рыбку съесть узнать количество элементов для аллокации соответствующего массива, и полный перебор отложить на более подходящее время.

Четыре новых метода DistinctBy / UnionBy / IntersectBy / ExceptBy теперь позволяют явно указывать поле-селектор:

А в дополнение к ним завезли ещё два аналогичных метода: MaxBy / MinBy .

Странно, что до этого не додумались раньше, но теперь это есть. Методы FirstOrDefault / LastOrDefault / SingleOrDefault позволяют указывать дефолтное значение, как это делается в методе nullable-типов GetValueOrDefault :

Ну и напоследок. Метод Zip теперь имеет перегрузку для итерации по трём коллекциям:

Дата и время

Борьба со временем — это не только метафора из жизни, это ещё и вполне себе реальное явление в мире разработки ПО. Довольно большая часть разработчиков просто не умеет правильно работать с датой и временем. Дополнительного масла в огонь невежества подливают фреймворки, предоставляя вместо правильных инструментов универсальные.

Разработчики .NET решили немного то ли облегчить, то ли усугубить страдания и добавили пару новых структур: DateOnly и TimeOnly , а также немного подшаманили с поддержкой временных зон и ещё по мелочи. Достаточно подробный обзор этих нововведений уже есть в этой переводной статье. И он обязателен к прочтению и глубокому осмыслению.

Preview Features и сразу Generic Math

Выпуск новых версий .NET уже давно встал на поток: в год — по LTS-версии. Это значительно быстрее, чем было раньше с классическим фреймворком, и это хорошо с одной стороны: можно оперативнее реагировать на запросы пользователей, быстрее выкатывать полезные фичи и вообще — не тормозить.

Но внезапно это породило и проблемы с некоторым особенно сложным в разработке функционалом. Разработчики .NET столкнулись с тем, что некоторые полезные новые фичи они физически не успевают выпустить, протестировать и внедрить во всех библиотеках за год. Не говоря уже о том, чтобы собрать фидбек по ним.

И они придумали механизм Preview Features. Теперь LTS-версию .NET можно будет поставлять с недоделанными превью-фичами. То, что раньше было доступно только в превью- и RC-версиях фреймворка, отныне может совершенно легально попасть в библиотеки, компиляторы и продакшн. В целях безопасности это всё обвешано атрибутами и настройками, чтобы по умолчанию быть выключенным. То есть, вы не сможете это использовать, специально не заморочившись. А вот захотите вы заморачиваться или нет — дело ваше.

Возможно, вам понравится первая превью-фича, для которой разработали весь этот механизм: статические абстрактные методы интерфейсов. Эта фича как раз из тех, что довольно сложно внедрить быстро. Её не успели обкатать в превью-версиях .NET 6 и решили выпустить в LTS-версии в том виде, в каком успеют реализовать к релизу. Поскольку это превью-фича, то нет никаких гарантий, что она не изменится даже в ближайших двух RC-выпусках .NET 6. Более того: нет никаких гарантий, что она не изменится в апдейтах .NET 6 после релиза. На этой новой фиче построен механизм арифметики в обобщениях. Детально об этом можно почитать в статье, и звучит это неплохо.

Итого, с помощью нового механизма превью-фич и разработчики .NET, и разработчики на .NET получают лишнее время на обкатку интересных идей. Вопрос: а не выльется ли это в конце концов в то же самое, во что превратились HTML и CSS? Ну, то есть, когда нумерация версий уже не имеет значения, а регулярно добавляемые фичи сначала какое-то время находятся в превью-стадии, а затем, после тестирования и доработок, просто переходят в спецификацию?

Больше анализаторов богу анализа!

С компилятором Roslyn наступила эра Roslyn-анализаторов, которые, вообще-то, здорово помогают в разработке. В .NET 5 в компилятор уже было встроено порядка 250 различных анализаторов, и ещё больше можно было скачать в виде nuget-пакетов. С какого-то момента команда dotnet build выводит дикое количество уведомлений от анализаторов о том, что разработчик говнокодит пишет что-то не то. С одной стороны, эти предупреждения, генерируемые анализаторами, помогают заметить косяки и сделать код чище. С другой стороны, всё равно есть много ложных срабатываний, в которых теряются действительно важные замечания. Это всё можно настроить, но на это нужно время.

В .NET 6 решили не останавливаться на достигнутом, и теперь встроенных анализаторов ещё больше (правда, среди них есть и те, что ранее поставлялись отдельно). Окинув беглым взглядом список новых анализаторов, нельзя не признать, что среди них, безусловно, много полезных.

В общем, читайте портянки компилятора во время вывода и настраивайте свою среду разработки так, чтобы не игнорировать подсказки анализаторов.

API для выделения памяти

Лёгким движением руки C# можно превратить в C. И это почти не шутка: в .NET 6 завезли нативное выделение памяти. За это дело отвечают специальные методы в новом классе System.Runtime.InteropServices.NativeMemory .

К чёрту управляемые ресурсы, к чёрту сборщик мусора. Да здравствуют alloc и free методы! Разумеется, всячески подчёркивается, что это для низкоуровневого кода и алгоритмов. Но мы как-то упустили упустили момент, когда C# стал позиционироваться как язык для таких вещей.

Разработчики очень серьёзно заигрались в оптимизацию. Пока нет единого мнения насчёт того, хорошо это или плохо. Фанаты оптимизаций приветствуют такие возможности. Но людей с определённым профессиональным опытом настораживает попытка создать универсальную платформу для всего сразу.

Что будет дальше? Добавление возможности писать прямые ассемблерные инструкции в коде? Или движение в сторону C++? Подождём .NET 7.

И так далее

Список нововведений в .NET куда больше, чем описано в этой статье. Но надо когда-нибудь остановиться, потому что описать все детали в одном посте — это перебор. Просто быстренько пройдёмся по оставшемуся:

  • пул потоков полностью переписан с нативного на управляемый код;
  • оптимизация работы со структурами: они теперь могут целиком передаваться как параметры через регистры процессора;
  • ускорено приведение и проверка интерфейсных типов (будет быстрее работать Pattern Matching);
  • с помощью прекрасной новой команды dotnet sdk check вы можете проверить актуальность ваших SDK;
  • вебсокеты поддерживают компрессию;
  • BigInteger теперь парсит строки почти на 90% быстрее;
  • Vector<T> теперь поддерживает примитивы nint и nuint ;
  • добавлена поддержка OpenTelemetry.

Но даже это не окончательный список.

А самое главное — за кадром остались нововведения в языке C# 10. Об этом — в другой раз.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *