.NET 8 вышел в релиз, значит можно начинать переносить свои проекты на новую версию. В этой статье мы рассмотрим новые улучшения и фишки: C# 12, производительность, Native AOT, GC, новые типы, направленные на повышение производительности, NuGet Audit и прочее.

1080_Whats_New_In_NET8_ru/image1.png

C# 12

По нововведениям C# 12 мы уже прошлись в отдельной статье. В ней мы затронули новые особенности языка: первичные конструкторы, параметры по умолчанию, collection expressions, inline массивов и остальное. В этот раз изменений не очень много. А как вы оцениваете обновление языка? Мы вот взглянули, и сразу появились идеи для новых правил C# анализатора.

Кстати, раз уж речь зашла про анализатор, сделаю небольшой анонс. Мы уже работаем над поддержкой .NET 8 и C# 12 — она появится в PVS-Studio 7.28. Релиз запланирован на начало декабря, и чтобы его не пропустить, приглашаю подписаться на рассылку пресс-релизов.

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

Microsoft сказали, что представленный в прошлом году .NET 7 очень быстрый, но .NET 8 быстрее. И это действительно так. Stephen Toub в присущей ему манере рассказал про улучшения производительности в .NET 8. Это статья на сотни страниц, охватывающая если не все, то большинство улучшений. Усовершенствования коснулись таких вещей, как JIT, GC, рефлексия, коллекции, LINQ, кодогенерация, сериализция и десериализция, примитивные типы и многое другое.

В статье очень большое внимание уделено сравнению производительности .NET 7 и .NET 8, много сравнительных таблиц. Заходите почитать, я думаю, что каждый найдёт там что-то интересное.

Native AOT

Вкратце напомню об этой технологии. Если коротко, то Native AOT использует ahead of time компилятор для компиляции IL в машинный код во время публикации self-contained приложения. В .NET 8 добавлена поддержка архитектур x64 и arm64 на macOS.

Native AOT приложения включают в себя .NET runtime. Исходя из этого, они имеют больший размер по сравнению с обычными приложениями. В .NET 8 улучшили и этот аспект. В таблице представлен размер для "Hello World" программы на .NET 7 и .NET 8:

Operating system

.NET 7

.NET 8

Linux x64 (with -p:StripSymbols=true)

3.76 MB

1.84 MB

Windows x64

2.85 MB

1.77 MB

Как видно из таблицы, размер приложения на Linux был заметно уменьшен – на целых 50%.

Также .NET 8 начинает работу по включению поддержки Native AOT для iOS подобных платформ. Стоит заметить, что это только начало работы, и разработчики просят не делать поспешных выводов о производительности. Теперь вы можете собирать и запускать .NET iOS и .NET MAUI приложения с Native AOT на следующих платформах: ios, iossimulator, maccatalyst, tvos и tvossimulator.

Новые типы, нацеленные на производительность

В .NET 8 были добавлены типы, нацеленные на производительность в различных сценариях. Теперь у разработчиков появились коллекции FrozenDictionary<TKey, TValue> и FrozenSet<T>, которые находятся в пространстве имён System.Collections.Frozen. Новые коллекции обеспечивают неизменяемость ключей или значений после создания. Это позволяет их специально оптимизировать для операций чтения. Довольно полезное нововведение для случаев, когда коллекция заполняется при первом использовании и хранится в течение длительного времени. Пример использования:

private static readonly FrozenDictionary<string, bool> _settings = 
  GetSettings().ToFrozenDictionary();
....
if (_settings.TryGetValue(key, out bool setting))
{
    //....
}

Добавлен новый тип SearchValues<T>. Он представляет неизменяемый набор элементов, доступный только для чтения, который оптимизирован для эффективного поиска.

Ещё один новый тип CompositeFormat появился специально для ситуаций, когда строки формата неизвестны на этапе компиляции. Например, если строка формата загружается из ресурсов.

Ну и напоследок появились новые типы XxHash3 и XxHash128, которые предоставляют реализацию быстрых алгоритмов хеширования XXH3 и XXH128.

NuGet Audit

Безопасность играет важную роль в разработке, и разработчики .NET не забывают про это. Теперь при выполнении dotnet add и dotnet restore вы будете получать предупреждения о каждом пакете, который содержит уязвимость.

Кстати говоря, PVS-Studio умеет искать уязвимые компоненты, которые используются в вашем проекте. Если используемая вами библиотека содержит в себе другую уязвимую библиотеку, анализатор также выдаст предупреждение. Таким образом, анализатор ищет не только прямые уязвимые зависимости, но и транзитивные. Подробнее про PVS-Studio как SCA-решение.

Random

Для работы со случайностью были добавлены новые методы:

Метод GetItems поможет случайно выбрать указанное количество элементов из переданного набора, а метод Shuffle перемешает переданную последовательность элементов.

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

Garbage collection

В .NET 8 была добавлена возможность регулирования ограничения памяти. Данная возможность может быть полезна для облачных сервисов. Например, при пониженной нагрузке можно уменьшить количество доступной памяти. Для регулирования лимитов нужно вызвать метод RefreshMemoryLimit.

Также теперь можно обновлять некоторые настройки конфигурации GC. Например, можно установить жёсткий предел для размера кучи:

AppContext.SetData("GCHeapHardLimit", _memoryLimit);
GC.RefreshMemoryLimit();

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

Сериализацию и десериализацию сильно улучшили с выходом нового .NET. Улучшений очень много, так что я постараюсь выделить основные.

Итак, улучшения:

  • подтянули производительность;

  • уменьшили размер Native AOT приложений, которые используют System.Text.Json;

  • кодогенератор теперь поддерживает сериализацию для required и init членов. Это уже поддерживалось в сериализации на основе рефлекции;

  • поддержка иерархии интерфейсов;

  • расширена функциональность атрибута JsonSourceGenerationOptionsAttribute;

  • теперь вы можете отключить сериализацию на основе рефлексии, которая включена по умолчанию.

Узнать про все добавленные функции, исправления багов и прочее вы можете из соответствующей статьи: "What's new in System.Text.Json in .NET 8".

Заключение

Вспоминая новые фишки .NET 7, кажется, что в .NET 8 появилось не так уж и много чего.

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

В статье перечислены только самые интересные нововведения, которые были бы полезны большинству разработчиков. Со всеми улучшениями вы можете ознакомиться здесь. Если вы нашли для себя что-то полезное и этого нет в статье, то пишите об этом комментариях.

Используете ли вы уже что-то из фишек нового .NET? Пишите в комментариях.

Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Artem Rovenskii. What's new in .NET 8?.

Комментарии (23)


  1. xmetropol
    14.11.2023 15:17

    новость хорошая, но на момент прочтения статьи последняя доступная для скачивания версия .net на официальной странице 8.0.0-rc2


    1. Heggi
      14.11.2023 15:17
      +4

      Свежие пакеты версии 8.0.0 только появляются в nuget. Думаю у них там CI/CD активно пашет. Ждем когда закончит)


    1. rip_m Автор
      14.11.2023 15:17
      +4

      Да, есть такой момент, но на момент публикации статьи через dotnet-install скрипт уже можно было установить 8.0.100. А теперь ещё и на официальной странице для скачивания появилась новая версия.


    1. a-tk
      14.11.2023 15:17
      -3

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

      PS: даже сегодня релизнулся



  1. Saladin
    14.11.2023 15:17
    +1

    Тауб, конечно, пишет томы, а не статьи. Интересная работа у человека.


    1. ryanl
      14.11.2023 15:17
      +1

      Тауб крут, ему на конфе 20 минут выделили, он подсветил 3 основные улучшения и перед каждой из них упоминал, что их там сотни. Видно, что владеет информацией лучше всех. Просто как пулемет без остановок рассказывал, поправлял и прочее. Он уже делает заметки по 9-ому дотнету. Коллеги заржали дружно, когда он про заметки на 9-ый дотнет упомянул. По перфомансу у них там серьезные мозги в MS сидят.


      1. UndefinedRef
        14.11.2023 15:17
        +1

        Никто не спорит, мозги у них сидят очень серьёзные, но случаются конечно иногда оказии
        https://fornever.me/en/posts/2023-10-21.code-vectorization.html


  1. LordDarklight
    14.11.2023 15:17

    Про новые фишки и изменения связанные с производительностью и оптимизации новой платформы почти ничего так и не рассказали, например о поддержке инструкций AVX-512. Или о пока не совсем понятном новом генераторе кода с технологией Dynamic Profile-Guided Optimization - что это, динамическая перекомпиляция кода в runtime на основе профилирования этого runtime?

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

    Интересно было бы узнать, что там нового по отельной устанавливаемым компонентам (пакетам) для новой платформы, например про изменения в многочисленных фронтэнд системах для .NET обновлённых для 8 версии платформы (ASP, Blazor, MAUI...), и про новый .NET Aspire для облачных технологий клиент-серверного online взаимодействия Cloud Native. Хотя тут, наверное, на каждую такую технологию надо свою отдельную статью писать. Но это если там изменений много....

    Не упомянули даже про улучшения поддержки контейнерной технологии.

    Написали про добавленную поддержку AOT на macOS - и тут же ниже привели таблицу с размерами файлов (жаль, только для консольного проекта и без размера занятого в памяти), но там только Windows и Linux, а macOS туда не включили.

    P.S.

    В языке C# 12 появились первичные конструкторы классов - это здорово. Но я так и не понял - есть какие-то ключевые отличия от аналогичных конструкторов для record? Вот явно напрашивается теперь сравнение лоб в лоб.

    Аргументы первичных конструкторов в классах создают что - поля? Всегда только поля? Всегда публичные или как-то можно это менять?

    И никак не могут быть просто аргументами, значения которых далее могут быть использованы в инициализации явных полей/свойств?

    P.P.S.

    Так ключевое слово field для обращения к теневым полям свойств так и не завезли? "Обещали" кода-то. Вроде бы даже в Roslyn компиляторе в потрохах есть намёки на их поддержку (если ничего не путаю). Никто не в курсе - планы ещё есть, и когда же?

    P.P.P.S.

    Псевдонимы без ограничений понравились - особенно для кортежей (а то ранее можно было только та using myname = System.TupleValue<T1,T2,T3,T4,T5,T6,T7,T8> - до 8 полей (последний должен быть кортежем) - но это не позволяло делать именованные поля, а теперь вроде бы можно по классическому объявлять кортежи.

    Да я понимаю - что ранее для таких случае предпочтительны были структуры, потом вообще record записи появились - просто синтаксис кортежей очень удобный, особенно как временный (на время разработки какого-то модуля программы) или для приватных каких-то вещей, или при кодогенерацию. Вот бы его ещё расширить оператором ".." для объединения нескольких кортежей, и итерацию по кортежу - вообще круто было бы


    1. ritorichesky_echpochmak
      14.11.2023 15:17

      Не жду сравнения лоб в лоб, но вот по аналогии с рекордами Non-destructive mutation было бы неплохо иметь для того чтобы быстро мапить объекты с синтаксисом вида

      var newObject = sourceObject with { NewCoolProperty = 42, AnotherOne = "test" };


    1. rip_m Автор
      14.11.2023 15:17
      +4

      Конечно, в статье не всё отражено, вставить всё просто невозможно. Что-то считаешь незначительным, что-то не для всех. Про производительность я соглашусь, но если начинать перечислять, то в комментарии всё равно кто-то придёт и напишет, что что-то забыли :). Поэтому решил ограничиться ссылкой на исчерпывающую статью. Тем более, лучше Тауба сложно сделать.
      А вот про отдельные устанавливаемые пакеты действительно лучше сделать отдельные статьи (хотя тогда это будет копирка статей Microsoft), так как здесь хотелось выделить более общие вещи.

      Спасибо, за большой комментарий. Постараемся в следующих статьях про .NET учесть.


  1. HOMPAIN
    14.11.2023 15:17

    Подскажите, не придумали в шарпе что-то для быстрого объявления потокобезопасных переменных? Что бы каждый get set не оборачивать в lock.


    1. a-tk
      14.11.2023 15:17

      Зависит от того, что надо.

      Варианты:

      • атрибут [Synchonization] существует с самого зарождения дотнета.

      • класс Interlocked для работы с простыми типами и со ссылками с тех же времён

      • класс ThreadLocal появился чуть позже

      • ещё есть контекст синхронизации для асинхронного кода

      Из Вашего вопроса не вполне понятно, что Вам на самом деле надо и что Вы хотите сделать. Может быть Вам вообще не нужна потокобезопасность...


      1. HOMPAIN
        14.11.2023 15:17
        +1

        Задача такая:

        У класса есть переменные, которые могут меняться и читаться в разных потоках: int , struct, String...

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

        private int badTempTarget;
        public int BadTempTarget { get { lock (Locker) { return badTempTarget; } } protected set { lock (Locker) { badTempTarget = value; } } }


        1. LordDarklight
          14.11.2023 15:17
          +1

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

          Но, всё-таки я не совсем прав - вероятно Int64 (который long) всё-таки не атомарен (не знаю почему так - возможно тут вся суть в битности системы - т.е. на 64битном процессоре это атомарный тип, а на 32-битном нет (всё дело в поддержке соответствующих инструкций самим процессором) - вопрос только где эти 32-битные процессоры сейчас есть; но может дело не только в процессоре а ещё и в режиме работы с ним ОС - т.е. ОС должна быть 64битной, чтобы эти операции были атомарны; ну и само собой int является 32 битным - и атомарным на любой (32битной или 64битной) ОС) - т.к. как есть Interlocked.Read для данного типа (в т.ч. беззнакового). Само собой все 128-битные числа (включая decimal) тоже не атомарны!

          Аналогичная история есть про 64-битный double - хотя он, в отличии, от int в .NET всегда 64битный, и для него нет функции Interlocked.Read (как и для decimal).

          Указатели 64битные (в 64битном приложении) - для них всё тоже самое что для Int64, и 32 битные (в 32 битном приложении) - для них всё тоже самое что для Int32.

          Ну а для присвоения значения никаких особых функций нет - но вероятно блокировка нужна - если тип не атомарный (чтобы коррелировать с блокировкой на чтение, но если применяется Interlocked.Read то не нужна блокировка при установке значения; или надо Interlocked.Exchange применять в этих случаях)

          Пусть меня поправят, кто лучше разбирается в неблокирующей многопоточности NET

          ну или вот тут почитать

          P.S.

          Правда при многопоточности, кроме атомарности есть ещё понятие кеша процессора - но тут в C# есть ключевое слово модификатор volatile при объявлении переменных и полей, и ещё Thread.VolitileRead - тут уже для любой битности это имеет значение (не надо только с Interlocked.Read сочетать)


          1. LordDarklight
            14.11.2023 15:17

            Дополнение:

            Thread.VolitileRead  устарела - заменена на

            Volatile.Read

            так же есть и

            Volatile.Write

            Но это всё как раз блокирующие операции (правда не на всех процессорах)

            • Переменные операции чтения и записи гарантируют, что значение считывается или записывается в память, а не кэшируется (например, в регистре процессора). Таким образом, эти операции можно использовать для синхронизации доступа к полю, которое может быть обновлено другим потоком или оборудованием.

            • Класс Volatile также предоставляет операции чтения и записи для некоторых 64-разрядных типов, таких как Int64 и Double. Переменные операции чтения и записи в такой 64-разрядной памяти являются атомарными даже на 32-разрядных процессорах, в отличие от обычных операций чтения и записи.

            • В C# использование модификатора volatile для поля гарантирует, что каждый доступ к нему является энергонезависимой операцией памяти, но модификатор volatile не может применяться к элементам массива. Методы Volatile.Read и Volatile.Write можно использовать в элементах массива.

            но тут нигде нет пока поддержки для 128 битных типов decimal и Int128 :-( для них пока только lock


      1. ryanl
        14.11.2023 15:17

        Контекст синхронизации - это не про потокобезопасность.


    1. ryanl
      14.11.2023 15:17
      +1

      С таким видением сложного вопроса как у вас можно просить Маска кнопку "Полететь на Марс".


  1. lightman
    14.11.2023 15:17
    +1

    Господа, а встроенная поддержка JSON содержит все функции Newtonsoft JSON.NET, можно переходить?

    И те, кто уже перешёл, как она по производительности по сравнению с ней?


    1. Zagrebelion
      14.11.2023 15:17
      +1

      https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft?pivots=dotnet-8-0
      Есть официальный migration guide с таблицей фич, эквивалентов и workaroundов; можете сами посмотреть, хватит ли в вашей ситуации.


    1. Ilay_Developer
      14.11.2023 15:17
      +1

      Да. Всё есть и скорость намного выше.


    1. nronnie
      14.11.2023 15:17

      Насколько я помню его не планировали как замену Newtonsoft, поэтому оттуда как раз и убрали редко используемые вещи, чтобы сделать его более легковесным и быстрым. И Джеймс Ньютон (автор "старого" Newtonsoft), на самом деле, он как раз один из авторов "нового" System.Text.Json.


  1. nronnie
    14.11.2023 15:17

    Очень грустно всегда читать ваши статьи, потому что знаешь что завтра тебе снова идти на работу, где больше половины коллег даже про какой-нибудь yield не знают :)) Пытался недавно сделать кое-какие DTO-шки на record и на ревью с помоями смешали: "Деды наши обходились безо всяких record." На прошлом проекте было запрещено использовать LINQ потому что тимлид его не знал.