Пишем парсер query string на C#!
Нужно разобрать строку вида ?page=2&tag=cs&tag=api и получить структуру, где повторяющиеся ключи не перетирают друг друга.
В этой задаче:
Такой маленький парсер полезен для CLI-утилит, тестов и логов.
➡️ C# Ready | #задача
Нужно разобрать строку вида ?page=2&tag=cs&tag=api и получить структуру, где повторяющиеся ключи не перетирают друг друга.
В этой задаче:
• убираем лишний вопросительный знак
• делим строку на пары
• декодируем ключи и значения
Такой маленький парсер полезен для CLI-утилит, тестов и логов.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4👍4🔥3
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5❤4👍1
Почему ToDictionary может упасть на обычных данных?
Когда нужно быстро превратить список в словарь, часто используют ToDictionary. Это удобно, пока ключи уникальные.
Например, есть список пользователей и нужно быстро искать их по email.
Код короткий и читаемый. Но он содержит скрытое предположение, что одинаковых email в списке точно нет.
Если из API, CSV или базы прилетят два пользователя с одним ключом, ToDictionary бросит исключение. Он не может сам решить, какой объект оставить.
Если дубли возможны, сначала нужно явно выбрать правило. Например, оставить первый элемент.
Теперь поведение видно прямо в коде. Мы не случайно теряем данные, а осознанно выбираем первый объект для каждого email.
Если важнее оставить последний элемент, правило тоже должно быть явным.
А если нужно сохранить все значения одного ключа, лучше использовать ToLookup.
Lookup похож на словарь, но один ключ может содержать несколько элементов. Это хорошо подходит для тегов, ролей, заказов пользователя и результатов группировки.
Иногда правильнее вообще не чинить дубли в коде, а остановить импорт и показать проблему.
Такой вариант полезен, если email должен быть уникальным по бизнес-правилу. Тогда исключение лучше заменить на понятную ошибку валидации.
Вывод простой. ToDictionary хорош, когда уникальность гарантирована. Если данные приходят извне, сначала реши, что делать с дублями.
В таком случае comparer лучше указать явно.
Без этого можно получить два разных ключа, хотя с точки зрения бизнеса это один и тот же пользователь.
Если данные приходят из внешнего источника, полезно сначала нормализовать ключи.
Но нормализация тоже должна быть осознанной. Для одних полей это правильно, для других можно случайно сломать смысл значения.
➡️ C# Ready | #совет
Когда нужно быстро превратить список в словарь, часто используют 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();
Но нормализация тоже должна быть осознанной. Для одних полей это правильно, для других можно случайно сломать смысл значения.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍4🔥3
Интересная статья про разработку игры на C# без большого движка!
Автор показывает, как подойти к созданию игры ближе к железу и базовой архитектуре, чтобы лучше понимать цикл обновления, отрисовку и устройство игровых объектов.
В статье автор показывает:
• как думать о game loop и состоянии сцены
• почему полезно понимать основу без готового движка
• какие части игры приходится собирать руками
➡️ C# Ready | #статья
Автор показывает, как подойти к созданию игры ближе к железу и базовой архитектуре, чтобы лучше понимать цикл обновления, отрисовку и устройство игровых объектов.
В статье автор показывает:
• как думать о game loop и состоянии сцены
• почему полезно понимать основу без готового движка
• какие части игры приходится собирать руками
Продолжай читать на Habr
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👍4🔥2
Настраиваем JSON-сериализацию через System.Text.Json!
Во многих API нужно отдавать JSON в понятном формате. Часто это camelCase-поля, аккуратные отступы в debug-режиме и отсутствие лишних null-значений.
Для начала подключим пространство имён:
Создадим простую модель:
Теперь заведём настройки сериализации:
Включим camelCase для имён свойств:
Если JSON нужно читать глазами, включим форматирование:
Теперь сериализуем объект:
Обратно объект можно получить так:
Важно не создавать настройки в каждом горячем вызове. Лучше вынести их в статическое поле или общий сервис, чтобы код был единообразным и без лишних аллокаций.
➡️ C# Ready | #практика
Во многих 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);
Важно не создавать настройки в каждом горячем вызове. Лучше вынести их в статическое поле или общий сервис, чтобы код был единообразным и без лишних аллокаций.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍4🔥2
Собираем автообновление конфига в C#!
Нужно следить за config.json и автоматически перечитывать настройки после изменения файла. Это удобно для сервисов, внутренних тулов и приложений, где часть параметров можно менять без перезапуска.
В этой задаче:
Debounce нужен потому, что одно сохранение файла часто порождает несколько событий подряд. Без небольшой задержки обработчик может попытаться прочитать файл прямо во время записи.
➡️ C# Ready | #задача
Нужно следить за config.json и автоматически перечитывать настройки после изменения файла. Это удобно для сервисов, внутренних тулов и приложений, где часть параметров можно менять без перезапуска.
В этой задаче:
• создаём FileSystemWatcher
• фильтруем события по имени файла
• добавляем короткий debounce
Debounce нужен потому, что одно сохранение файла часто порождает несколько событий подряд. Без небольшой задержки обработчик может попытаться прочитать файл прямо во время записи.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤7🔥5
Знали, как ограничить количество параллельных async-операций через SemaphoreSlim?
Иногда нужно обработать много элементов, отправить HTTP-запросы, проверить файлы, обновить записи или вызвать внешний API. Кажется логичным запустить всё через Task.WhenAll.
Пример выглядит компактно:
Но если items тысяча, код может создать тысячу одновременных операций. Для внешнего сервиса это может закончиться rate limit, таймаутами, большим расходом памяти и странными ошибками под нагрузкой.
Для ограничения параллельности удобно использовать SemaphoreSlim:
Число 5 означает, что одновременно внутрь защищённого участка попадут только пять операций. Остальные будут ждать свою очередь асинхронно, без блокировки потока.
В обработке это выглядит так:
finally здесь важен. Если SendAsync завершится с ошибкой, Release всё равно выполнится, и очередь не зависнет навсегда из-за занятого слота.
Полный вариант часто заворачивают в Select:
Такой подход хорошо подходит для интеграций, batch-обработки, фоновых задач и любых мест, где нужно контролировать давление на внешнюю систему.
➡️ C# Ready | #совет
Иногда нужно обработать много элементов, отправить 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-обработки, фоновых задач и любых мест, где нужно контролировать давление на внешнюю систему.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤5🔥3