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
Как индексация строк изменила систему типов 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
Как код C# превращается в машинные инструкции!

Схема показывает весь путь от исходного файла до выполнения процессором. Компилятор C# сначала создаёт промежуточный IL и сборку, а не готовые инструкции для конкретного процессора.

При запуске CLR загружает сборку. Когда вызывается метод, JIT-компилятор переводит его IL в машинный код для текущей платформы.

Сохрани, чтобы не потерять!

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

При неудачном совпадении шаблоны с вложенными повторами могут проверять множество вариантов. На пользовательском вводе такая обратная проверка способна надолго загрузить процессор:
var pattern = "^(a+)+$";
var regex = new Regex(pattern);


По умолчанию Regex может работать без ограничения времени. Для обычного движка задаём конечный timeout при создании объекта:
var regex = new Regex(pattern, RegexOptions.None,
    TimeSpan.FromMilliseconds(100));


Если поиск не уложится в интервал, будет выброшено RegexMatchTimeoutException. Обрабатываем её отдельно и возвращаем понятную ошибку проверки:
try {
    return regex.IsMatch(input);
} catch (RegexMatchTimeoutException) {
    throw new ValidationException(
        "Regex validation timed out");
}


В .NET 7 и новее можно рассмотреть RegexOptions.NonBacktracking, если шаблон не использует неподдерживаемые конструкции вроде lookaround или backreference.

➡️ C# Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤4🔥3
❤7👍5🔥3
Добавляем замер времени HTTP-запросов через DelegatingHandler!

Когда несколько сервисов обращаются к внешним API, можно видеть статус и длительность каждого запроса. Вынесем замер в обработчик HttpClient, чтобы не дублировать Stopwatch в каждом месте вызова.

Создаём обработчик, который получает логгер через конструктор:
sealed class TimingHandler(
    ILogger<TimingHandler> log) :
    DelegatingHandler
{
}


DelegatingHandler передаёт запрос следующему обработчику цепочки. Параметр primary constructor доступен методам класса, поэтому отдельное поле для log не требуется.

Переопределяем асинхронную отправку и сохраняем контракт базового класса:
protected override
    Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken token)


Токен отмены передаём дальше без замены, чтобы отмена вызывающего кода дошла до сетевого запроса.

Перед отправкой запускаем секундомер и готовим поле статуса:
var timer = Stopwatch.StartNew();
HttpStatusCode? status = null;


Переменная status остаётся null, если транспорт завершился исключением до получения ответа. Это позволяет логировать и успешные ответы, и неудачные попытки соединения.

Выполняем запрос через следующий обработчик и запоминаем статус:
try
{
    var response = await base.SendAsync(
        request, token);
    status = response.StatusCode;
    return response;
}


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

В finally записываем метод, статус и длительность:
finally
{
    log.LogInformation(
        "{Method} {Status} {Ms}ms",
        request.Method, status,
        timer.ElapsedMilliseconds);
}


Регистрируем обработчик как transient и добавляем его в именованный клиент:
builder.Services.AddTransient<TimingHandler>();
builder.Services.AddHttpClient("catalog")
    .AddHttpMessageHandler<TimingHandler>();


Фабрика создаёт цепочки обработчиков для клиентов, поэтому один экземпляр DelegatingHandler нельзя переиспользовать в нескольких цепочках.

➡️ C# Ready | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4👍4🔥3
Почему Task.Run не делает синхронный ввод-вывод асинхронным?

Оборачивать блокирующее чтение файла в Task.Run кажется способом освободить текущий поток, но ожидание всё равно занимает поток из пула:
await Task.Run(() => File.ReadAllText(path));


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

Для чтения файла используем API с настоящей асинхронной операцией:
string text = await File.ReadAllTextAsync(
    path, cancellationToken);


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

Токен отмены передаём и в сам метод чтения, и дальше по цепочке:
await using var stream = File.OpenRead(path);
using var reader = new StreamReader(stream);
string text = await reader.ReadToEndAsync(token);


Для CPU-вычислений Task.Run может быть уместен, например чтобы не блокировать UI-поток:
var result = await Task.Run(
    () => Calculate(input, token), token);


Синхронная функция должна сама проверять отмену, потому что токен не прерывает уже запущенный код:
foreach (var item in items)
{
    token.ThrowIfCancellationRequested();
    Process(item);
}


Независимые операции с асинхронным API запускаем напрямую и ожидаем вместе:
Task<string> config = File.ReadAllTextAsync(
    configPath);
Task<string> data = File.ReadAllTextAsync(
    dataPath);
await Task.WhenAll(config, data);


Так ожидание I/O не расходует потоки на блокировку. В серверном коде Task.Run вокруг обычного ввода-вывода чаще добавляет переключение потоков, а не пропускную способность.

➡️ C# Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👍4🔥2
Строим порядок сборки из зависимостей!

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

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


Алгоритм Кана проходит каждую задачу и каждую связь один раз.

➡️ C# Ready | #задача
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤3🔥3
Строим ограниченную очередь фоновых задач через Channel<T>!

Если задания поступают быстрее, чем обрабатываются, неограниченная очередь продолжает расти и расходует память. Bounded channel задаёт верхний предел, а производитель асинхронно ждёт свободное место.

Создаём очередь на 32 задания:
var queue = Channel.CreateBounded<Job>(
    new BoundedChannelOptions(32)
    { FullMode = BoundedChannelFullMode.Wait });


Режим Wait не отбрасывает новые элементы при заполнении. Вместо этого WriteAsync приостанавливает производителя до появления свободного слота.

Передаём задания с поддержкой отмены:
await queue.Writer.WriteAsync(job, token);
await queue.Writer.WriteAsync(nextJob, token);


Токен позволяет отменить ожидание записи, если приложение завершает операцию. В реальном producer-коде задания обычно записываются в цикле, а ошибки производства обрабатываются отдельно.

Потребитель читает элементы по мере появления:
await foreach (var job in queue.Reader
    .ReadAllAsync(token))
{
    await HandleAsync(job, token);
}


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

Для нескольких производителей можно завершить запись после их окончания:
try { await ProduceAsync(queue.Writer, token); }
finally { queue.Writer.TryComplete(); }


TryComplete закрывает очередь для новых записей, но позволяет обработать уже добавленные задания. Если producer завершился с исключением, его можно передать в TryComplete(error), чтобы consumer увидел причину остановки.

Запускаем производство и чтение вместе:
var consumer = ConsumeAsync(queue.Reader, token);
Exception? error = null;
try { await ProduceAsync(queue.Writer, token); }
catch (Exception ex) { error = ex; }
queue.Writer.TryComplete(error);
await consumer;


Ёмкость 32 ограничивает только число ожидающих элементов, но не время обработки.

➡️ C# Ready | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍4🔥2
Шпаргалка по class, record и struct в C#!

Например, class и record class являются ссылочными типами, а struct и record struct передают значения как отдельные данные.

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

Сохрани, чтобы не потерять!

➡️ C# Ready | #шпора
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥17👍8❤4🤝4
❤6👍4🔥4
Почему ValueTask не стоит использовать ради ускорения?

ValueTask часто воспринимают как более быстрый Task. На практике он полезен не всегда и может сделать код сложнее.

Главный сценарий для ValueTask появляется, когда результат часто уже готов синхронно. Например, значение нашлось в памяти и не нужно создавать полноценный Task.

Такой метод может вернуть готовое значение без лишней аллокации:
if (cache.TryGetValue(id, out var user))
{
return new ValueTask(user);
}


А если значения нет, можно перейти к обычной async-операции:
return new ValueTask(LoadAsync(id));


Но у ValueTask есть ограничения. Его нельзя бездумно ждать несколько раз, сохранять на потом или передавать по цепочке как обычный Task.

Плохой пример выглядит так:
var result = GetUserAsync(id);
await result;
await result;


С Task такое обычно безопасно, а с ValueTask можно получить неожиданное поведение, особенно если внутри используется IValueTaskSource.

Если метод почти всегда делает настоящий async I/O, лучше оставить Task:
Task LoadUserAsync(int id)
{
return db.Users.FindAsync(id).AsTask();
}


ValueTask используйте только там, где на то есть причина и польза.

➡️ C# Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4👍2🔥2
Media is too big
VIEW IN TELEGRAM
CSharpGuide по C# и .NET!

Каталог разбит на шесть этапов, основы языка, управление потоком, методы и коллекции, ООП, продвинутый C# и практический .NET. Внутри этапов собраны отдельные уроки по переменным, LINQ, async/await, JSON, тестированию и другим темам.

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

Оставляю ссылочку на CSharpGuide


➡️ C# Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
❤10👍9🔥4