.NET 6 в процессе разработки, и я хотел поделиться некоторыми из моих любимых новых API в .NET и ASP.NET Core, которые вам обязательно понравятся. Почему они понравятся? Потому что они разработаны при прямом участии нашего фантастического сообщества разработчиков .NET. Подробнее в статье.

Чтение и запись файлов

В .NET 6 появился новый низкоуровневый API, позволяющий читать/записывать файлы без использования FileStream. Он также поддерживает scatter/gather IO (несколько буферов) и дублирующиеся чтение и запись при заданном смещении файла (file offset).

using Microsoft.Win32.SafeHandles;
using SafeFileHandle handle = File.OpenHandle("ConsoleApp128.exe");
long length = RandomAccess.GetLength(handle);

Console.WriteLine(length);

Путь и идентификатор процесса

Есть несколько новых способов доступа к пути и идентификатору процесса без выделения нового объекта процесса:

int pid = Environment.ProcessId;
string path = Environment.ProcessPath;

Console.WriteLine(pid);
Console.WriteLine(path);

CSPNG (криптографически безопасный генератор псевдослучайных чисел)

Генерация случайных чисел из CSPNG (криптографически безопасного генератора псевдослучайных чисел) стала проще, чем когда-либо:

// Дай мне 200 случайных bytes 
byte[] bytes = RandomNumberGenerator.GetBytes(200);

Parallel.ForEachAsync

Мы добавили Parallel.ForEachAsync, способ планирования асинхронной работы, который позволяет вам контролировать степень параллелизма:

var urlsToDownload = new [] 
{
    "https://dotnet.microsoft.com",
    "https://www.microsoft.com",
    "https://twitter.com/davidfowl"
};

var client = new HttpClient();

await Parallel.ForEachAsync(urlsToDownload, async (url, token) =>
{
    var targetPath = Path.Combine(Path.GetTempPath(), "http_cache", url);

    HttpResponseMessage response = await client.GetAsync(url);

    if (response.IsSuccessStatusCode)
    {
        using FileStream target = File.OpenWrite(targetPath);

        await response.Content.CopyToAsync(target);
    }

Помощники по настройке

Мы добавили помощник, чтобы упростить сброс, если отсутствует требуемый раздел конфигурации:

var configuration = new ConfigurationManager();
var options = new MyOptions();

configuration.GetRequiredSection("MyOptions").Bind(options);

class MyOptions
{
    public string? SettingValue { get; set;}

LINQ

Также есть масса новых методов LINQ. Вот новый помощник для разбиения любого IEnumerable на пакеты:

int chunkNumber = 1;
foreach (int[] chunk in Enumerable.Range(0, 9).Chunk(3))
{
    Console.WriteLine($"Chunk {chunkNumber++}");
    foreach (var item in chunk)
    {
        Console.WriteLine(item);
    }
}

Еще больше LINQ

Больше LINQ! Теперь есть методы MaxBy и MinBy:

var people = GetPeople();

var oldest = people.MaxBy(p => p.Age);
var youngest = people.MinBy(p => p.Age);

Console.WriteLine($"The oldest person is {oldest.Age}");
Console.WriteLine($"The youngest person is {youngest.Age}");

public record Person(string Name, int Age);

Вторая степень

Не держите математику в голове? И я нет. Вот несколько новых помощников для работы со второй степенью:

using System.Numerics;

uint bufferSize = 235;
if (!BitOperations.IsPow2(bufferSize))
{
    bufferSize = BitOperations.RoundUpToPowerOf2(bufferSize);
}

Console.WriteLine(bufferSize);

Улучшения WaitAsync

Теперь существует гораздо более простой (и правильно реализованный) способ ожидания асинхронного завершения задачи. Следующий код вернет await, если он не был завершен в течение 10 секунд. Операция может еще продолжаться. Это для неотменяемых операций.

Task operationTask = SomeLongRunningOperationAsync();

await operationTask.WaitAsync(TimeSpan.FromSeconds(10));

ThrowIfNull

Больше не нужно проверять значение null в каждом методе перед тем, как сбросить исключение. Теперь это всего лишь одна строка кода.

void DoSomethingUseful(object obj)
{
    ArgumentNullException.ThrowIfNull(obj);
}

Работа с NativeMemory

using System.Runtime.InteropServices;

unsafe
{
    byte* buffer = (byte*)NativeMemory.Alloc(100);

    NativeMemory.Free(buffer);
}

Обработка сигналов Posix

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

using System.Runtime.InteropServices;

var tcs = new TaskCompletionSource();

PosixSignalRegistration.Create(PosixSignal.SIGTERM, context =>
{
    Console.WriteLine($"{context.Signal} fired");
    tcs.TrySetResult();
});

await tcs.Task;

Новый API метрик

Мы добавили совершенно новый API метрик, основанный на @opentelemetry в .NET 6. Он поддерживает измерения, суперэффективен и будет иметь экспортеры для популярных сервисов метрик.

using System.Diagnostics.Metrics;

// Вот как вы производите метрики 

var meter = new Meter("Microsoft.AspNetCore", "v1.0");
Counter<int> counter = meter.CreateCounter<int>("Requests");

var app = WebApplication.Create(args);

app.Use((context, next) =>
{
    counter.Add(1, KeyValuePair.Create<string, object?>("path", context.Request.Path.ToString()));
    return next(context);
});

app.MapGet("/", () => "Hello World");

Вы даже можете listen и meter:

var listener = new MeterListener();
listener.InstrumentPublished = (instrument, meterListener) =>
{
    if(instrument.Name == "Requests" && instrument.Meter.Name == "Microsoft.AspNetCore")
    {
        meterListener.EnableMeasurementEvents(instrument, null);
    }
};

listener.SetMeasurementEventCallback<int>((instrument, measurement, tags, state) =>
{
    Console.WriteLine($"Instrument: {instrument.Name} has recorded the measurement: {measurement}");
});

listener.Start();

API современного таймера

И последнее, но не менее важное: современный API таймера (я думаю, что это уже пятый API таймера в .NET). Он полностью асинхронен и не подвержен типам ошибок, с которыми сталкиваются другие таймеры, например, проблемами времени жизни объекта, отсутствием асинхронных обратных вызовов и т.д.

var timer = new PeriodicTimer(TimeSpan.FromSeconds(1));

while (await timer.WaitForNextTickAsync())
{
    Console.WriteLine(DateTime.UtcNow);
}

Summary

Это всего лишь несколько примеров новых API, которые появятся в .NET 6. Для получения дополнительной информации см. Различия API, примечаний к выпуску .NET 6. Кроме того, Стивен только что написал впечатляющий пост об улучшении производительности в .NET6, так что обязательно прочтите его. Наконец, не забудьте скачать предварительную версию .NET 6 и опробовать новые API уже сегодня.

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


  1. pygubanov
    24.08.2021 16:42
    +4

    И чем отличается новый System.Runtime.InteropServices.NativeMemory.Alloc(x) от старого System.Runtime.InteropServices.Marshal.AllocHGlobal(x)


    1. deitry
      24.08.2021 17:05
      +4

      Нагуглил аналогичный вопрос: https://devblogs.microsoft.com/dotnet/new-dotnet-6-apis-driven-by-the-developer-community/#comment-10236

      Вкратце ответ:

      "Marshal.AllocHGlobal по документации должна (что не может быть изменено чтобы не ломать обратную совместимость) вызывать LocalAlloc Win32. LocalAllocAPI считается легаси и не рекомендуется к использованию в современных приложениях

      С другой стороны NativeMemory.Alloc  по документации является сравнительно тонкой обёрткой над  malloc. Соответственно она должна быть быстрее и более совместимой со стандартной логикой выделения/возвращения памяти в современных приложениях"


      1. a-tk
        25.08.2021 16:04

        И возникает интересный вопрос: Marshal.AllocHGlobal вызывает LocalAlloc, и есть WinAPI-шная функция GlobalAlloc...


  1. anonymous
    00.00.0000 00:00


    1. AgentFire
      24.08.2021 19:19

      Да и смысл, если есть nullable context.


      1. pankraty
        24.08.2021 21:58
        +1

        Nullable типы не являются стопроцентной защитой, т.к. а) выдают ворнинги, а не ошибки компиляции, б) могут быть подавлены восклицательнвм знаком, в) компилятор иногда ошибается в определении nullability, г) бесполезны при вызове из контекста с отключенными nullable типами. Поэтому, чтобы не отхватить NRE в неожиданном месте, может потребоваться дополритеььный контроль входных параметров


        1. AgentFire
          25.08.2021 15:00
          -1

          Ну не отхватишь NRE в том месте, где он падает, отхватишь в начале метода. В обоих случаях все равно полезешь смотреть StackTrace и исправлять баг.

          Как бы да, nullable context пока форсированно не внедрен, но, во первых, никто не запрещает поставить соответствующий флаг компиляции, а во-вторых, он решают проблему на корню, вместо того чтобы писать тонны костылей в виде ThrowIfNull.


        1. pankraty
          25.08.2021 07:15

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


        1. a-tk
          28.08.2021 09:52

          Отчего ж не верить. Аналогичные вещи бывают в каждом первом проекте.

          Другое дело что лучше сделать что-то вида T NotNull<T>(this T obj) where T : class


  1. anonymous
    00.00.0000 00:00


    1. pankraty
      24.08.2021 21:54

      У этих двух способов есть небольшое различие в том, как считается тестовое покрытие: в вашем способе надо два теста, чтобы обеспечить 100% покрытие, а в способе из статьи - достаточно одного.

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


  1. anonymous
    00.00.0000 00:00


  1. anonymous
    00.00.0000 00:00


    1. a-tk
      25.08.2021 16:06

      а ещё можно сделать метод-расширение вида obj.NotNull()


  1. anonymous
    00.00.0000 00:00


  1. anonymous
    00.00.0000 00:00


  1. a-tk
    28.08.2021 10:52

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


  1. anonymous
    00.00.0000 00:00


  1. a-tk
    29.08.2021 12:55
    -2

    Настолько компактно, что бесполезно. Что Вы дальше с этой проверкой делать собираетесь? Вот это дальше уже можно и выносить куда-то в тихое место.


  1. anonymous
    00.00.0000 00:00


  1. a-tk
    29.08.2021 13:12
    -2

    Я подразумевал, что исключение бросит, если null, а Вы?


    1. mayorovp
      29.08.2021 13:48

      Согласно (почти) общепринятым соглашениям, этот метод должен вернуть bool. Метод, который бросил бы исключение, назывался бы AssertNotNull или ThrowIfNull.


    1. a-tk
      29.08.2021 16:11
      -1

      Как бы там ни было, это короче чем в каждой строчке писать код вида this.field = argument ?? throw new ArgumentNullException()

      А как оно будет называться - дело десятое. Главное чтобы одинаково в пределах проекта/команды/компании.


  1. anonymous
    00.00.0000 00:00


  1. anonymous
    00.00.0000 00:00


  1. mayorovp
    24.08.2021 19:06
    +3

    Мы добавили помощник, чтобы упростить сброс, если отсутствует требуемый раздел конфигурации

    Моя не понимать что значить "помошник" и "сброс"


    1. AgentFire
      24.08.2021 19:20

      лучше бы добавили поддержку record-ов для конфигурационных моделей, делов-то конструктор вычитать.


      1. BashkaMen
        24.08.2021 19:57

        да, и вообще IConfiguration не удобен, банально когда нужно достать конфиг во время DI регистраций, там уже нужно танцевать или делать что то на конвеншенах


        1. Ordos
          24.08.2021 22:40
          -1

          Если речь про ASP.NET Core, то там есть хак, которым сами MS часто пользуются. Поскольку IConfiguration лежит в IServiceCollection в виде инстанса, то можно его просто оттуда достать и использовать. Такая же тема с IHostEnvironment. Такой хак позволяет иногда сильно упростить сигнатуры методов.


          1. BashkaMen
            24.08.2021 22:43

            то что его можно инжектить это понятно, я про использование его в ConfigureServices, а еще хуже когда у вас екстеншн метод от другой библиотеки services.AddSomeService("section"), где как должна выглядеть секция знает только дока


            1. AgentFire
              25.08.2021 00:52

              Ну нет, "где и как лежит", если речь про конфиг-хранилище, то задача это достать и распарсить лежит на том, кому это хранилище принадлежит, обычно это исполняемая (exe) сборка. А если у вас "другой библиотеке" требуется какая-то конфигурация, то она должна "попросить" ее через свой интерфейс или тот же record.


          1. mayorovp
            24.08.2021 22:48

            Получить IConfiguration не проблема, проблема "заглянуть" внутрь — потому что биндер до опций на этом этапе ещё не настроен.


            1. AgentFire
              25.08.2021 00:53
              -1

              Опции как-то сильно переоценены. Из полезного разве что монитор горячих изменений. Но он как раз в configure service не нужен.


  1. anonymous
    00.00.0000 00:00


  1. AgentFire
    25.08.2021 14:56

    Я и не предлагаю вместо IOptions<MyServiceOptions> прокидывать IConfiguration, это же дичь. Достаточно простой дэтэошки в виде того же record MyServiceOptions. Ее мокать ваще проще простого.


  1. Alexrook
    24.08.2021 22:21
    -3

    К чему все это, если куча компаний до сих пор сидит на Win7 и, дай бог, NET Framework 4. Почему? А потому, что обновиться - это целое состояние. Ну и если и так все работает, то к чему что-то менять? Видимо еще лет 10-20 будут сидеть на 7ке. И еще спасибо, что не на XP. Хотя лет 5 назад я встречал уникумов и с XP. А вместе с этим и разработчики будут смотреть на NET 6 и только облизываться, копаясь тем временем в жестком легаси и даже разрабатывая что-то с нуля, будут подстраиваться под реалии заказчика с его давно устаревшими операционками и фреймворками.


    1. AgentFire
      25.08.2021 00:54
      -3

      Их же собственные половые трудности. В свое время обновление было беплатным.


      1. Alexrook
        25.08.2021 20:52

        Для любой конторы - это все равно не совсем бесплатно. Это, как минимум, человекочасы сисадминов, которых и так стараются держать по минимуму, так что они и текущие задачи не всегда успевают вовремя выполнять. Тем более тут нужна воля руководства, а оно о бесплатных обновлениях обычно ничего не знает, а любой сисадмин промолчит, так как знает чем это чревато. Себе же лишнюю работу придумает. А ему это надо?

        А понимание приходит лишь тогда, когда что-то уже перестает работать на 7ке, что-то уже не обновить, что-то вообще не установить. И этот процесс пока только начинается. Пока раздавали бесплатно (кстати, я не уверен, что эта акция была и для корпоративных пользователей), все работало и на 7ке без проблем. Тем более была развернута целая компания про то, какое это фуфло винда 10я против родной 7ки. Даже у меня такие знакомые были, которые ни в какую не хотели воспринимать 10ку. Правда жизнь заставила и последний адепт 7ки среди моих знакомых сдался пару лет назад. А о будущем мало кто задумывается. А теперь, когда это и сложно осуществить и очень дорого, это стараются оттягивать на самый последний момент, когда 7ка по-видимому будет как сейчас даже не XP.


        1. AgentFire
          25.08.2021 22:57

          Ну я и говорю, их же собственные трудности.


          1. Alexrook
            26.08.2021 20:04
            -1

            Нет, не их трудности. Им вообще плевать, работает у пользователя что-то или нет. А так же им плевать на каких технологиях им будут реализовывать их хотелки. Само руководство обычно не сидит за компом весь день. Зачастую их жизнь проходит в разъездах, встречах и переговорах, для которых у них всегда под рукой личный новенький макбук с самыми последними версиями софта. Ну а то, что офисные работники пытаются выжать последние возможности из древних ОС и устаревшего софта, их волнует мало. Да что там говорить, многие даже офисные компы обновлять особо не торопятся. Какое-то время назад работал в конторе, где компы уже не обновляли лет 15. Настолько там было все древнее и тормозное, что работать было просто невозможно. К сожалению, отечественные руководители не видят связи между модернизацией железа и обновлением софта и производительностью человека. Для них нормально, что тот же сервер 1С работает с такой скоростью, что ты большую часть времени видишь курсор ожидания, чем реально работаешь. Нажал кнопку? Жди. Хочешь открыть документ? Жди. Хочешь элементарный отчет? Жди. То же самое на рабочем месте, лишь в меньших масштабах. Все лагает и тормозит, но пока хоть как-то работает, руководство все устраивает.


  1. anonymous
    00.00.0000 00:00


  1. derikn_mike
    25.08.2021 00:27
    -3

    public static List<List<T>> ChunkByCount<T>(this List<T> msgs, int count){ return msgs .Select((x, i) => new { Index = i, Value = x }) .GroupBy(x => x.Index / count) .Select(x => x.Select(v => v.Value).ToList()) .ToList();}

    вот и всё прощай мой самый залайканый код на стэковерфлове


    1. mayorovp
      25.08.2021 00:34
      +1

      Но это же ужас, а не код. Столько лишних действий...


      1. derikn_mike
        25.08.2021 20:11
        -5

        че ты пишешь? открой новый нативный и удивись. вернёшься и мне лайк еще поставишь...


        1. mayorovp
          25.08.2021 21:30

          "Нативный" — это какой? Если вы про вот эти — Chunk.cs#L55-L78 и Chunk.SpeedOpt.cs#L11-L37 — то они гораздо оптимальнее работают.


          1. a-tk
            26.08.2021 14:14
            +2

            Ещё бы остаток от деления убрать для полного счастья.


    1. AgentFire
      25.08.2021 00:56
      +3

      Группировка, потом ToList, потом ToList, да и зачем вообще List возвращать в коде; сложность — что по CPU, что по памяти, в итоге хорошо если O(3N)?


  1. anonymous
    00.00.0000 00:00


  1. Magals
    25.08.2021 06:38

    Картинка на превью меня все же настораивает, если предполагаете писать кросс-платформненое приложение с использованием MAUI, то что будет с Blazor hybrid mobile, так как мы его уже выбрали?