.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)
anonymous
00.00.0000 00:00AgentFire
24.08.2021 19:19Да и смысл, если есть nullable context.
pankraty
24.08.2021 21:58+1Nullable типы не являются стопроцентной защитой, т.к. а) выдают ворнинги, а не ошибки компиляции, б) могут быть подавлены восклицательнвм знаком, в) компилятор иногда ошибается в определении nullability, г) бесполезны при вызове из контекста с отключенными nullable типами. Поэтому, чтобы не отхватить NRE в неожиданном месте, может потребоваться дополритеььный контроль входных параметров
AgentFire
25.08.2021 15:00-1Ну не отхватишь NRE в том месте, где он падает, отхватишь в начале метода. В обоих случаях все равно полезешь смотреть StackTrace и исправлять баг.
Как бы да, nullable context пока форсированно не внедрен, но, во первых, никто не запрещает поставить соответствующий флаг компиляции, а во-вторых, он решают проблему на корню, вместо того чтобы писать тонны костылей в виде
ThrowIfNull
.
pankraty
25.08.2021 07:15Я с вами согласен, и мне лично спокойнее, когда все ветки кода покрыты. Просто пытался придумать обоснование, когда один способ может оказаться предпочтительнее другого.
a-tk
28.08.2021 09:52Отчего ж не верить. Аналогичные вещи бывают в каждом первом проекте.
Другое дело что лучше сделать что-то вида
T NotNull<T>(this T obj) where T : class
anonymous
00.00.0000 00:00pankraty
24.08.2021 21:54У этих двух способов есть небольшое различие в том, как считается тестовое покрытие: в вашем способе надо два теста, чтобы обеспечить 100% покрытие, а в способе из статьи - достаточно одного.
Оставим за скобками целесообразность расчёта этого показателя. На некоторых проектах требования по уровню покрытия внедрены в пайплайн сборки, и хочешь не хочешь, но вынужден соблюдать требования
a-tk
28.08.2021 10:52Примерно затем же, что и автосвойства: язык предоставляет возможность более компактно описать решения для распространённых задач. А коль скоро стандартная библиотека не предлагает нормальных средств для этого, рождаются такие вот костыли.
a-tk
29.08.2021 12:55-2Настолько компактно, что бесполезно. Что Вы дальше с этой проверкой делать собираетесь? Вот это дальше уже можно и выносить куда-то в тихое место.
a-tk
29.08.2021 13:12-2Я подразумевал, что исключение бросит, если null, а Вы?
mayorovp
29.08.2021 13:48Согласно (почти) общепринятым соглашениям, этот метод должен вернуть bool. Метод, который бросил бы исключение, назывался бы AssertNotNull или ThrowIfNull.
a-tk
29.08.2021 16:11-1Как бы там ни было, это короче чем в каждой строчке писать код вида
this.field = argument ?? throw new ArgumentNullException()
А как оно будет называться - дело десятое. Главное чтобы одинаково в пределах проекта/команды/компании.
mayorovp
24.08.2021 19:06+3Мы добавили помощник, чтобы упростить сброс, если отсутствует требуемый раздел конфигурации
Моя не понимать что значить "помошник" и "сброс"
AgentFire
24.08.2021 19:20лучше бы добавили поддержку record-ов для конфигурационных моделей, делов-то конструктор вычитать.
BashkaMen
24.08.2021 19:57да, и вообще IConfiguration не удобен, банально когда нужно достать конфиг во время DI регистраций, там уже нужно танцевать или делать что то на конвеншенах
Ordos
24.08.2021 22:40-1Если речь про ASP.NET Core, то там есть хак, которым сами MS часто пользуются. Поскольку IConfiguration лежит в IServiceCollection в виде инстанса, то можно его просто оттуда достать и использовать. Такая же тема с IHostEnvironment. Такой хак позволяет иногда сильно упростить сигнатуры методов.
BashkaMen
24.08.2021 22:43то что его можно инжектить это понятно, я про использование его в ConfigureServices, а еще хуже когда у вас екстеншн метод от другой библиотеки services.AddSomeService("section"), где как должна выглядеть секция знает только дока
AgentFire
25.08.2021 00:52Ну нет, "где и как лежит", если речь про конфиг-хранилище, то задача это достать и распарсить лежит на том, кому это хранилище принадлежит, обычно это исполняемая (exe) сборка. А если у вас "другой библиотеке" требуется какая-то конфигурация, то она должна "попросить" ее через свой интерфейс или тот же record.
mayorovp
24.08.2021 22:48Получить IConfiguration не проблема, проблема "заглянуть" внутрь — потому что биндер до опций на этом этапе ещё не настроен.
AgentFire
25.08.2021 00:53-1Опции как-то сильно переоценены. Из полезного разве что монитор горячих изменений. Но он как раз в configure service не нужен.
AgentFire
25.08.2021 14:56Я и не предлагаю вместо
IOptions<MyServiceOptions>
прокидыватьIConfiguration
, это же дичь. Достаточно простой дэтэошки в виде того жеrecord MyServiceOptions
. Ее мокать ваще проще простого.
Alexrook
24.08.2021 22:21-3К чему все это, если куча компаний до сих пор сидит на Win7 и, дай бог, NET Framework 4. Почему? А потому, что обновиться - это целое состояние. Ну и если и так все работает, то к чему что-то менять? Видимо еще лет 10-20 будут сидеть на 7ке. И еще спасибо, что не на XP. Хотя лет 5 назад я встречал уникумов и с XP. А вместе с этим и разработчики будут смотреть на NET 6 и только облизываться, копаясь тем временем в жестком легаси и даже разрабатывая что-то с нуля, будут подстраиваться под реалии заказчика с его давно устаревшими операционками и фреймворками.
AgentFire
25.08.2021 00:54-3Их же собственные половые трудности. В свое время обновление было беплатным.
Alexrook
25.08.2021 20:52Для любой конторы - это все равно не совсем бесплатно. Это, как минимум, человекочасы сисадминов, которых и так стараются держать по минимуму, так что они и текущие задачи не всегда успевают вовремя выполнять. Тем более тут нужна воля руководства, а оно о бесплатных обновлениях обычно ничего не знает, а любой сисадмин промолчит, так как знает чем это чревато. Себе же лишнюю работу придумает. А ему это надо?
А понимание приходит лишь тогда, когда что-то уже перестает работать на 7ке, что-то уже не обновить, что-то вообще не установить. И этот процесс пока только начинается. Пока раздавали бесплатно (кстати, я не уверен, что эта акция была и для корпоративных пользователей), все работало и на 7ке без проблем. Тем более была развернута целая компания про то, какое это фуфло винда 10я против родной 7ки. Даже у меня такие знакомые были, которые ни в какую не хотели воспринимать 10ку. Правда жизнь заставила и последний адепт 7ки среди моих знакомых сдался пару лет назад. А о будущем мало кто задумывается. А теперь, когда это и сложно осуществить и очень дорого, это стараются оттягивать на самый последний момент, когда 7ка по-видимому будет как сейчас даже не XP.
AgentFire
25.08.2021 22:57Ну я и говорю, их же собственные трудности.
Alexrook
26.08.2021 20:04-1Нет, не их трудности. Им вообще плевать, работает у пользователя что-то или нет. А так же им плевать на каких технологиях им будут реализовывать их хотелки. Само руководство обычно не сидит за компом весь день. Зачастую их жизнь проходит в разъездах, встречах и переговорах, для которых у них всегда под рукой личный новенький макбук с самыми последними версиями софта. Ну а то, что офисные работники пытаются выжать последние возможности из древних ОС и устаревшего софта, их волнует мало. Да что там говорить, многие даже офисные компы обновлять особо не торопятся. Какое-то время назад работал в конторе, где компы уже не обновляли лет 15. Настолько там было все древнее и тормозное, что работать было просто невозможно. К сожалению, отечественные руководители не видят связи между модернизацией железа и обновлением софта и производительностью человека. Для них нормально, что тот же сервер 1С работает с такой скоростью, что ты большую часть времени видишь курсор ожидания, чем реально работаешь. Нажал кнопку? Жди. Хочешь открыть документ? Жди. Хочешь элементарный отчет? Жди. То же самое на рабочем месте, лишь в меньших масштабах. Все лагает и тормозит, но пока хоть как-то работает, руководство все устраивает.
derikn_mike
25.08.2021 00:27-3public 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();}
вот и всё прощай мой самый залайканый код на стэковерфлове
mayorovp
25.08.2021 00:34+1Но это же ужас, а не код. Столько лишних действий...
derikn_mike
25.08.2021 20:11-5че ты пишешь? открой новый нативный и удивись. вернёшься и мне лайк еще поставишь...
mayorovp
25.08.2021 21:30"Нативный" — это какой? Если вы про вот эти — Chunk.cs#L55-L78 и Chunk.SpeedOpt.cs#L11-L37 — то они гораздо оптимальнее работают.
AgentFire
25.08.2021 00:56+3Группировка, потом ToList, потом ToList, да и зачем вообще List возвращать в коде; сложность — что по CPU, что по памяти, в итоге хорошо если O(3N)?
Magals
25.08.2021 06:38Картинка на превью меня все же настораивает, если предполагаете писать кросс-платформненое приложение с использованием MAUI, то что будет с Blazor hybrid mobile, так как мы его уже выбрали?
pygubanov
И чем отличается новый System.Runtime.InteropServices.NativeMemory.Alloc(x) от старого System.Runtime.InteropServices.Marshal.AllocHGlobal(x)
deitry
Нагуглил аналогичный вопрос: https://devblogs.microsoft.com/dotnet/new-dotnet-6-apis-driven-by-the-developer-community/#comment-10236
Вкратце ответ:
"
Marshal.AllocHGlobal
по документации должна (что не может быть изменено чтобы не ломать обратную совместимость) вызыватьLocalAlloc
Win32.LocalAlloc
API считается легаси и не рекомендуется к использованию в современных приложенияхС другой стороны
NativeMemory.Alloc
по документации является сравнительно тонкой обёрткой надmalloc
. Соответственно она должна быть быстрее и более совместимой со стандартной логикой выделения/возвращения памяти в современных приложениях"a-tk
И возникает интересный вопрос:
Marshal.AllocHGlobal
вызываетLocalAlloc
, и есть WinAPI-шная функцияGlobalAlloc
...