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

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

РКН: https://clck.ru/3SBaT3
Download Telegram
Пишем парсер query string на C#!

Нужно разобрать строку вида ?page=2&tag=cs&tag=api и получить структуру, где повторяющиеся ключи не перетирают друг друга.

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


Такой маленький парсер полезен для CLI-утилит, тестов и логов.

➡️ C# Ready | #задача
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
4👍4🔥3
Разбираем 7 приёмов для кеширования данных!

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

➡️ C# Ready | #шпора
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥54👍1
Почему ToDictionary может упасть на обычных данных?

Когда нужно быстро превратить список в словарь, часто используют ToDictionary. Это удобно, пока ключи уникальные.

Например, есть список пользователей и нужно быстро искать их по email.
var byEmail = users.ToDictionary(u => u.Email);


Код короткий и читаемый. Но он содержит скрытое предположение, что одинаковых email в списке точно нет.

Если из API, CSV или базы прилетят два пользователя с одним ключом, ToDictionary бросит исключение. Он не может сам решить, какой объект оставить.

Если дубли возможны, сначала нужно явно выбрать правило. Например, оставить первый элемент.
var byEmail = users
.GroupBy(u => u.Email)
.ToDictionary(g => g.Key, g => g.First());


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

Если важнее оставить последний элемент, правило тоже должно быть явным.
var byEmail = users
.GroupBy(u => u.Email)
.ToDictionary(g => g.Key, g => g.Last());


А если нужно сохранить все значения одного ключа, лучше использовать ToLookup.
var byEmail = users.ToLookup(u => u.Email);


Lookup похож на словарь, но один ключ может содержать несколько элементов. Это хорошо подходит для тегов, ролей, заказов пользователя и результатов группировки.

Иногда правильнее вообще не чинить дубли в коде, а остановить импорт и показать проблему.
var duplicates = users
.GroupBy(u => u.Email)
.Where(g => g.Count() > 1);


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

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

В таком случае comparer лучше указать явно.
var byEmail = users
.GroupBy(u => u.Email, StringComparer.OrdinalIgnoreCase)
.ToDictionary(g => g.Key, g => g.First(), StringComparer.OrdinalIgnoreCase);


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

Если данные приходят из внешнего источника, полезно сначала нормализовать ключи.
var email = user.Email.Trim().ToLowerInvariant();


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

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

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

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

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


➡️ C# Ready | #статья
Please open Telegram to view this post
VIEW IN TELEGRAM
5👍4🔥2
Настраиваем JSON-сериализацию через System.Text.Json!

Во многих API нужно отдавать JSON в понятном формате. Часто это camelCase-поля, аккуратные отступы в debug-режиме и отсутствие лишних null-значений.

Для начала подключим пространство имён:

using System.Text.Json;


Создадим простую модель:

record User(int Id, string Name, string? Email);


Теперь заведём настройки сериализации:

var options = new JsonSerializerOptions();


Включим camelCase для имён свойств:

options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;


Если JSON нужно читать глазами, включим форматирование:

options.WriteIndented = true;


Теперь сериализуем объект:

var json = JsonSerializer.Serialize(user, options);


Обратно объект можно получить так:

var copy = JsonSerializer.Deserialize<User>(json, options);


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

➡️ C# Ready | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
7👍4🔥2
Собираем автообновление конфига в 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