C# Ready | Unity
10.6K subscribers
1.52K photos
85 videos
757 links
Авторский канал по разработке на C# и Unity.
Ресурсы, гайды, задачи, шпаргалки.
Информация ежедневно пополняется!

Cотрудничество: @energy_c

Реклама на бирже: https://telega.in/channels/csharp_ready/card

РКН: https://clck.ru/3SBaT3
Download Telegram
Добавляем Request-Id middleware в ASP.NET Core!

Нужно связывать логи, ошибки и ответы API одним идентификатором запроса. Реализуем middleware, который берёт Request-Id из заголовка или создаёт новый.

В этой задаче:
• читаем входящий заголовок
• создаём fallback через Guid
• прокидываем значение в response
• подключаем middleware в pipeline


Такой подход помогает быстрее искать конкретный запрос в логах, трассировке, gateway и клиентских жалобах.

➡️ C# Ready | #задача
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤4🔥3
Шпаргалка по регулярным выражениям в .NET!

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

На картинке есть IsMatch, Match, Matches, Replace, Split, Count, EnumerateMatches, Escape и Unescape. Также показаны RegexOptions и тайм-аут, который ограничивает время поиска при неудачном шаблоне.

➡️ C# Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤3🔥3
Зачем передавать TimeProvider вместо прямого вызова DateTimeOffset.UtcNow?

Если код сам читает системные часы, тест зависит от момента запуска. Передай источник времени явно:
public sealed class ExpirationService(TimeProvider clock)
{
    public bool IsExpired(DateTimeOffset expiresAt) =>
        clock.GetUtcNow() >= expiresAt;
}


Сервис теперь не привязан к часам машины. В приложении используй системный провайдер:
services.AddSingleton<TimeProvider>(TimeProvider.System);


В тесте установи фиксированное время через FakeTimeProvider:
var clock = new FakeTimeProvider(
    new DateTimeOffset(2030, 1, 1, 12, 0, 0, TimeSpan.Zero));
var service = new ExpirationService(clock);


Создай дату истечения и сдвинь виртуальные часы до границы:
var expiresAt = clock.GetUtcNow().AddMinutes(5);
clock.Advance(TimeSpan.FromMinutes(5));
Assert.True(service.IsExpired(expiresAt));


Тест выполняется сразу и точно проверяет границу. Добавь пакет для управляемых часов:
dotnet add package Microsoft.Extensions.TimeProvider.Testing


Для управляемых задержек передай тот же провайдер в Task.Delay:
await Task.Delay(TimeSpan.FromMinutes(5), clock, cancellationToken);


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

➡️ C# Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤5🔥4
Обрабатываем список запросов параллельно с ограничением!

Представь, что нужно отправить тысячи записей во внешний API. Последовательный цикл может работать слишком долго, а запуск тысячи Task сразу создаст всплеск соединений и нагрузки. Соберём обработчик, который ограничивает число одновременных операций и умеет штатно отменяться.

Сначала задаём лимит параллелизма и общий токен отмены:
var options = new ParallelOptions
{
    MaxDegreeOfParallelism = 4,
    CancellationToken = cancellationToken
};


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

Передаём каждую запись в асинхронную операцию и не блокируем поток через Wait или Result:
await Parallel.ForEachAsync(records, options, async (record, token) =>
{
    await client.PostAsJsonAsync("/events", record, token);
});


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

Соберём результат ответа и явно обработаем неуспешный HTTP-статус:
await Parallel.ForEachAsync(records, options, async (record, token) =>
{
    using var response = await client.PostAsJsonAsync(
        "/events", record, token);
    response.EnsureSuccessStatusCode();
});


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

Запусти обработку внутри обычного try и отличай отмену от сбоя сервера:
try
{
    await Parallel.ForEachAsync(records, options, ProcessAsync);
}


Ожидаемую отмену обработай отдельно от ошибок сервера:
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
{
    logger.LogInformation("Обработка отменена");
}


Сетевые ошибки можно записать отдельно или передать вызывающему уровню:
catch (HttpRequestException exception)
{
    logger.LogError(exception, "Не удалось отправить запись");
}


Parallel.ForEachAsync подходит для независимых элементов, когда важен ограниченный параллелизм. Лимит не задаёт частоту запросов в секунду, не сохраняет порядок завершения и сам по себе не реализует повторы.

➡️ C# Ready | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍3🔥3
Проверяем заголовок PNG и читаем размеры!

Нужно быстро узнать размеры изображения до декодирования всего файла. В этой задаче читаем фиксированные 24 байта, проверяем сигнатуру PNG и первый чанк IHDR, затем получаем ширину и высоту.

В решении:
• читаем заголовок без загрузки изображения целиком
• отклоняем усечённый или неверный файл
• декодируем размеры с учётом порядка байтов


Такой код подходит для предварительной проверки загрузок и очередей обработки изображений.

➡️ C# Ready | #задача
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤4🔥4
🔥12❤3👍3😁1
Шпаргалка по сериализации JSON в C#!

Source generation создаёт код сериализации во время сборки и помогает проектам с trimming и Native AOT. В контекст добавляются используемые модели, а вызовы получают сгенерированный JsonTypeInfo без поиска метаданных через reflection.

➡️ C# Ready | #шпора
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7🔥5👍3🤝2
Знали, почему долгоживущий HttpClient может обращаться по устаревшему DNS-адресу?

HttpClient повторно использует соединения из пула, а DNS обычно проверяется при установке нового соединения. Если адрес сервиса изменился, уже открытое соединение не обязано сразу переключиться на новый IP.

Создаём общий клиент и задаём срок жизни соединений:
static readonly HttpClient Client = new(
    new SocketsHttpHandler
    {
        PooledConnectionLifetime =
            TimeSpan.FromMinutes(5)
    });


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

Клиент повторно используем между запросами:
using var response =
    await Client.GetAsync(url, cancellationToken);
response.EnsureSuccessStatusCode();


Создание нового HttpClient на каждый запрос может постоянно плодить пулы соединений и привести к исчерпанию сокетов. Для приложений с DI вместо статического клиента подходит IHttpClientFactory с настройкой handler lifetime.

➡️ C# Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍3🔥3
Как индексация строк изменила систему типов HydraScript!

Автор добавил длину и обращение по индексу в интерпретируемый язык на C#. Работа над строками показала, что правила операций удобнее хранить рядом с типами, а не разносить по семантическому анализатору.

В статье разбирают:
• как интерфейс IOperator описывает допустимость операции и тип результата
• почему строки и массивы могут использовать общие правила индексации
• где проходит граница между проверкой типов и выполнением выражения

Такой подход упрощает развитие анализатора, но оставляет диагностику ошибок и контекст исходного кода в соответствующем слое.

Продолжай читать на Habr!


➡️ C# Ready | #статья
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5🔥4👍3
Запускаем периодическую проверку без наложения запросов!

Обычный таймер может вызвать следующий callback, пока предыдущая проверка ещё ждёт сеть. Сегодня соберём последовательный polling через PeriodicTimer и передадим отмену внутрь операции.

Создаём таймер с нужным интервалом:
using var timer = new PeriodicTimer(
    TimeSpan.FromSeconds(15));


PeriodicTimer допускает одного ожидающего потребителя. Если очередная проверка длится дольше интервала, накопившиеся тики не создают параллельные копии запроса.

Ждём тик и завершаем цикл через cancellation token:
while (await timer.WaitForNextTickAsync(token))
{
    await RefreshAsync(token);
}


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

Внутри проверки передаём тот же token сетевому вызову:
using var response = await client.GetAsync(
    endpoint, token);
response.EnsureSuccessStatusCode();


При остановке фоновой службы token прервёт ожидание тика и поддерживающий отмену HTTP-запрос.

➡️ C# Ready | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8👍4🔥4