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

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

РКН: https://clck.ru/3SBaT3
Download Telegram
Собираем автообновление конфига в C#!

Нужно следить за config.json и автоматически перечитывать настройки после изменения файла. Это удобно для сервисов, внутренних тулов и приложений, где часть параметров можно менять без перезапуска.

В этой задаче:
• создаём FileSystemWatcher
• фильтруем события по имени файла
• добавляем короткий debounce


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

➡️ C# Ready | #задача
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍87🔥5
Шпаргалка по middleware pipeline в ASP.NET Core!

Например, Use добавляет middleware в цепочку обработки, Run завершает pipeline, а Map и MapWhen позволяют разветвлять обработку по пути или условию.

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

➡️ C# Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥63👍1
Знали, как ограничить количество параллельных async-операций через SemaphoreSlim?

Иногда нужно обработать много элементов, отправить HTTP-запросы, проверить файлы, обновить записи или вызвать внешний API. Кажется логичным запустить всё через Task.WhenAll.

Пример выглядит компактно:
await Task.WhenAll(items.Select(SendAsync));


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

Для ограничения параллельности удобно использовать SemaphoreSlim:
using var gate = new SemaphoreSlim(5);


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

В обработке это выглядит так:
await gate.WaitAsync();
try
{
await SendAsync(item);
}
finally
{
gate.Release();
}


finally здесь важен. Если SendAsync завершится с ошибкой, Release всё равно выполнится, и очередь не зависнет навсегда из-за занятого слота.

Полный вариант часто заворачивают в Select:
var tasks = items.Select(async item =>
{
await gate.WaitAsync();
try { await SendAsync(item); }
finally { gate.Release(); }
});

await Task.WhenAll(tasks);


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

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

Например, \d помогает искать цифры, \w - символы слова, ^ и $ привязывают шаблон к началу и концу строки, а группы позволяют вытаскивать нужные части совпадения.

На картинке собраны основные элементы Regex в .NET. Есть классы символов, квантификаторы, anchors, группы, inline options, backreferences, замены и методы из System.Text.RegularExpressions.Regex.

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

➡️ C# Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
👍63🔥2
8👍4🔥3
Полезная статья про Avalonia-приложение для Raspberry Pi!

Автор разбирает создание кроссплатформенного приложения на Avalonia и показывает, как связать UI, сборку и доставку проекта через GitHub Actions.

В статье автор показывает:
• как устроить проект на Avalonia под разные платформы
• как подготовить сборку и публикацию через GitHub Actions
• какие нюансы появляются при запуске приложения на Raspberry Pi

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


➡️ C# Ready | #статья
Please open Telegram to view this post
VIEW IN TELEGRAM
6👍6🔥4
Собираем простой in-memory event bus в C#!

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

Для такой задачи можно собрать маленький event bus на делегатах.

Сначала опишем событие:
public sealed record OrderCreated(
int OrderId,
decimal Amount
);


Теперь заведём хранилище обработчиков. Ключом будет тип события, а значением список функций:
private readonly Dictionary<Type, List<Func<object, Task>>> _handlers = new();


Метод подписки принимает обработчик конкретного типа:
public void Subscribe<TEvent>(Func<TEvent, Task> handler)
{
var type = typeof(TEvent);

if (!_handlers.ContainsKey(type))
{
_handlers[type] = new();
}

_handlers[type].Add(e => handler((TEvent)e));
}


Здесь внутри мы приводим object обратно к TEvent. Снаружи код остаётся типобезопасным, потому что Subscribe принимает Func<TEvent, Task>.

Теперь добавим публикацию события:
public async Task Publish<TEvent>(TEvent evt)
{
var type = typeof(TEvent);

if (!_handlers.TryGetValue(type, out var handlers))
{
return;
}

foreach (var handler in handlers)
{
await handler(evt!);
}
}


Использование получается коротким:
var bus = new EventBus();

bus.Subscribe<OrderCreated>(e =>
{
Console.WriteLine($"Order {e.OrderId}");
return Task.CompletedTask;
});


Теперь можно отправить событие:
await bus.Publish(new OrderCreated(42, 1990m));


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

➡️ C# Ready | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
8👍5🔥3
Шпаргалка по throw и throw ex в C#!

Например, обычный throw внутри catch пробрасывает исходное исключение и сохраняет stack trace. А throw ex создаёт повторный выброс так, что место реальной ошибки становится сложнее найти.

На картинке показана разница между двумя вариантами. Есть сохранение stack trace, потеря исходного контекста, влияние на отладку и короткая таблица с правильным вариантом для catch-блоков.

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

➡️ C# Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
5👍4🔥2
Делаем потокобезопасный счётчик через Interlocked!

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

Обычный инкремент выглядит невинно:
_count++;


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

Для таких счётчиков в .NET есть Interlocked:
private int _count;

Interlocked.Increment(ref _count);


Теперь увеличение выполняется атомарно. Несколько потоков могут вызывать этот код одновременно, а итоговое значение не потеряет часть инкрементов.

Если нужно прибавить произвольное число, есть Add:
Interlocked.Add(ref _count, batchSize);


А для чтения текущего значения можно использовать CompareExchange без изменения:
int current = Interlocked.CompareExchange(ref _count, 0, 0);


Для long есть отдельные перегрузки, и это часто удобно для метрик, которые могут быстро расти:
private long _bytes;

Interlocked.Add(ref _bytes, size);


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

➡️ C# Ready | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
👍53🔥2
Напоминалка по async/await в C#!

Например, await приостанавливает выполнение метода до завершения Task, а Task.WhenAll() помогает дождаться нескольких независимых операций.
На картинке основные идеи async/await: зачем нужна асинхронность, что делают async и await, какие бывают return types, как запускать задачи параллельно и каких ошибок стоит избегать.

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

➡️ C# Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
8👍2🔥2
Парсим простой .env-файл на C#!

В небольших утилитах часто нужно прочитать настройки из файла вида KEY=value: токены, режим запуска, адрес API или путь к директории.

Пример строки:
var line = "APP_ENV=prod";


Разделим её по первому знаку =:
var index = line.IndexOf('=');

var key = line[..index];
var value = line[(index + 1)..];


Сохраним результат в словарь:
var config = new Dictionary<string, string>();
config[key] = value;


Теперь обработаем несколько строк:
foreach (var raw in File.ReadLines(".env"))
{
var line = raw.Trim();

if (line.Length == 0 || line.StartsWith("#"))
continue;

var index = line.IndexOf('=');

if (index <= 0)
continue;

config[line[..index]] = line[(index + 1)..];
}


Пример .env:
APP_ENV=prod
PORT=8080
DEBUG=false


Использование:
Console.WriteLine(config["APP_ENV"]);
Console.WriteLine(config["PORT"]);


Ожидаемый вывод:
prod
8080


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

➡️ C# Ready | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
5👍3🔥1
Разбираем immutable collections в C#!

Immutable collections помогают передавать данные без риска, что другой участок кода внезапно изменит коллекцию. Это удобно для конфигов, состояния UI, кешей и событий.

В этой шпоре собраны ImmutableArray, ImmutableList, Add, Remove, SetItem, ToImmutableArray и Builder.

➡️ C# Ready | #шпора
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥95👍3
12🔥5👍2
Разбираем LINQ: 7 операторов для повседневной выборки данных!

LINQ помогает писать обработку коллекций компактно и выразительно: фильтровать, преобразовывать, сортировать, группировать и искать элементы без ручных циклов. Эти операторы чаще всего встречаются в прикладном C#-коде, API, сервисах и отчётах.

➡️ C# Ready | #шпора
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍127🔥5🤝4