csharp_ready | Unsorted

Telegram-канал csharp_ready - C# Ready | Unity

9818

Авторский канал по разработке на C# и Unity. Ресурсы, гайды, задачи, шпаргалки. Информация ежедневно пополняется! Автор: @energy_it РКН: https://clck.ru/3SBaT3 Реклама на бирже: https://telega.in/c/csharp_ready

Subscribe to a channel

C# Ready | Unity

Шпаргалка по class, struct и record в C#!

Например, class передаётся по ссылке, struct хранит значение, record class даёт value equality для модели данных, а record struct совмещает value type с удобствами record.

На картинке сравниваются class, struct, record class и record struct. Есть тип хранения, значение по умолчанию, наследование, интерфейсы, immutability, equality, assignment и поведение ToString.

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

➡️ C# Ready | #ресурс

Читать полностью…

C# Ready | Unity

Разбираем immutable collections в C#!

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

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

➡️ C# Ready | #шпора

Читать полностью…

C# Ready | Unity

Напоминалка по async/await в C#!

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

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

➡️ C# Ready | #ресурс

Читать полностью…

C# Ready | Unity

Шпаргалка по throw и throw ex в C#!

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

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

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

➡️ C# Ready | #ресурс

Читать полностью…

C# Ready | Unity

Полезная статья про Avalonia-приложение для Raspberry Pi!

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

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

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


➡️ C# Ready | #статья

Читать полностью…

C# Ready | Unity

Знали, как ограничить количество параллельных 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 | #совет

Читать полностью…

C# Ready | Unity

Собираем автообновление конфига в C#!

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

В этой задаче:

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


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

➡️ C# Ready | #задача

Читать полностью…

C# Ready | Unity

Интересная статья про разработку игры на C# без большого движка!

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

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

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


➡️ C# Ready | #статья

Читать полностью…

C# Ready | Unity

Разбираем 7 приёмов для кеширования данных!

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

➡️ C# Ready | #шпора

Читать полностью…

C# Ready | Unity

Хороший вход в современный C# для начинающих и джунов!

Статья спокойно объясняет, зачем сейчас учить C#, где он применяется и с каких базовых вещей стоит начинать, если хочется двигаться в сторону .NET-разработки.

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

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


➡️ C# Ready | #статья

Читать полностью…

C# Ready | Unity

Почему не стоит lock-ать строку или публичный объект?

В C# lock работает по объекту-синхронизатору:

lock (sync)
{
// критическая секция
}


Проблема начинается, когда в роли sync используют строку:
lock ("cache")
{
UpdateCache();
}


Строки могут интернироваться, то есть одинаковые литералы могут ссылаться на один и тот же объект.

В итоге другой код в приложении тоже может случайно заблокироваться на "cache":
lock ("cache")
{
DoSomethingElse();
}


Похожая проблема с публичными объектами:
public object Sync = new();


Любой внешний код может взять этот объект и тоже сделать lock, создавая странные зависания.

Надёжнее держать приватный синхронизатор:
private readonly object _sync = new();

lock (_sync)
{
UpdateCache();
}


Так точка блокировки остаётся внутри класса, и никто снаружи не может случайно вмешаться.

➡️ C# Ready | #совет

Читать полностью…

C# Ready | Unity

Крутая статья о том, как подружить C#, .NET MAUI и Rust в одном приложении!

В этой статье:
• Как подключить Rust-код к MAUI-проекту
• Зачем может понадобиться нативная часть внутри C#-приложения
• Как C# и Rust могут вместе работать с графикой через SkiaSharp

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


➡️ C# Ready | #статья

Читать полностью…

C# Ready | Unity

Делаем простой rate limiter на C#!

Иногда нужно ограничить частоту действий: не давать пользователю слишком часто нажимать кнопку, вызывать команду или отправлять запрос

В этой задаче:

• храним timestamps последних вызовов;
• удаляем старые события;
• проверяем лимит в скользящем окне;


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

➡️ C# Ready | #задача

Читать полностью…

C# Ready | Unity

Собираем валидатор пароля на C#!

Нужно проверить пароль и показать пользователю понятные причины отказа? Сделаем небольшой валидатор, где каждое правило отвечает за одну проверку и возвращает конкретную ошибку.

В этой задаче:

• Описываем правила как функции;
• Проверяем длину, цифры, регистр и спецсимволы;
• Собираем все ошибки в один список.


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

➡️ C# Ready | #совет

Читать полностью…

C# Ready | Unity

Напоминалка по delegates, events и базовым конструкциям C#!

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

На картинке пример объявления delegate, регистрация методов, anonymous methods, события, популярные event handler-типы, а также рядом есть краткие блоки по классам, методам, struct, enum, свойствам и циклам.

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

➡️ C# Ready | #ресурс

Читать полностью…

C# Ready | Unity

Разбираем LINQ: 7 операторов для повседневной выборки данных!

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

➡️ C# Ready | #шпора

Читать полностью…

C# Ready | Unity

Парсим простой .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 | #практика

Читать полностью…

C# Ready | Unity

Делаем потокобезопасный счётчик через 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 | #практика

Читать полностью…

C# Ready | Unity

Собираем простой 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 | #практика

Читать полностью…

C# Ready | Unity

Шпаргалка по регулярным выражениям в .NET!

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

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

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

➡️ C# Ready | #ресурс

Читать полностью…

C# Ready | Unity

Шпаргалка по middleware pipeline в ASP.NET Core!

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

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

➡️ C# Ready | #ресурс

Читать полностью…

C# Ready | Unity

Настраиваем 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 | #практика

Читать полностью…

C# Ready | Unity

Почему 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 | #совет

Читать полностью…

C# Ready | Unity

Пишем парсер query string на C#!

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

В этой задаче:

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


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

➡️ C# Ready | #задача

Читать полностью…

C# Ready | Unity

Проверяем срок действия TLS-сертификата!

Нужно быстро понять, когда у сайта закончится HTTPS-сертификат? Соберём простую проверку через openssl: подключимся к серверу, достанем notAfter и посчитаем, сколько дней осталось до истечения.

В этой задаче:

• Получаем сертификат через openssl s_client;
• Достаём дату окончания через openssl x509;
• Считаем разницу и проверяем порог.


Такую проверку удобно запускать в cron, CI или простом мониторинговом скрипте, чтобы не узнать об истёкшем сертификате от пользователей.

➡️ C# Ready | #задача

Читать полностью…

C# Ready | Unity

7 дней бесплатного обучения frontend-разработке!

Получи востребованные навыки:

1. Генерация сайтов через Giga, DeepSeek, ChatGPT
2. Вёрстка страниц по макету на HTML + CSS
3. Оживление сайта анимацией элементов
4. Публикация сайта
5 Оформление портфолио
6. Поиск заказов
7. Лайфхаки по карьере

И советы по доработке проекта от опытных разработчиков.

Присоединяйся
👆

Читать полностью…

C# Ready | Unity

Разберём HttpClient: 7 возможностей для аккуратной работы с API!

HttpClient часто используют для интеграций, микросервисов и внешних HTTP-запросов. Чтобы код не превращался в ручную сборку строк, заголовков и JSON, стоит знать базовые возможности клиента.

В этой шпоре:

• базовый адрес через BaseAddress;
• общие заголовки;
• GET-запросы;


Эти вещи помогают писать API-клиенты короче, понятнее и устойчивее к зависаниям и ошибочным статусам.

➡️ C# Ready | #шпора

Читать полностью…

C# Ready | Unity

Полезная статья про Avalonia и реализацию MVVM на C# по шагам!

В этой статье:
• Что такое Avalonia и почему она интересна для кроссплатформенного desktop
• Как разделить интерфейс и бизнес-логику через MVVM
• Как постепенно собрать пример приложения с View, ViewModel, binding и данными

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


➡️ C# Ready | #статья

Читать полностью…

C# Ready | Unity

Делаем простой троттлинг действий в C#!

Иногда событие срабатывает слишком часто: ввод в поиске, resize окна, клики по кнопке или обновление фильтра. Чтобы не запускать тяжёлую операцию каждый раз, можно ограничить частоту выполнения.

Для начала создадим время последнего запуска:

DateTimeOffset lastRun = DateTimeOffset.MinValue;


И зададим минимальный интервал:
TimeSpan interval = TimeSpan.FromSeconds(2);


Теперь проверим, можно ли выполнять действие:
bool CanRun()
{
return DateTimeOffset.UtcNow - lastRun >= interval;
}


Когда действие реально выполняется, обновляем lastRun:
if (CanRun())
{
lastRun = DateTimeOffset.UtcNow;
Console.WriteLine("search");
}


Но удобнее завернуть это в небольшой класс:
sealed class Throttler
{
private DateTimeOffset _lastRun = DateTimeOffset.MinValue;
private readonly TimeSpan _interval;

public Throttler(TimeSpan interval)
{
_interval = interval;
}

public bool TryRun(Action action)
{
var now = DateTimeOffset.UtcNow;

if (now - _lastRun < _interval)
{
return false;
}

_lastRun = now;
action();
return true;
}
}


Использование получается простым:
var throttler = new Throttler(TimeSpan.FromSeconds(2));

throttler.TryRun(() => Console.WriteLine("send request"));
throttler.TryRun(() => Console.WriteLine("send request"));


Первый вызов выполнится, а второй сразу после него будет пропущен.

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

➡️ C# Ready | #практика

Читать полностью…

C# Ready | Unity

Полезная статья про создание Telegram-бота на C# с нуля!

В этой статье:
• Как зарегистрировать бота через BotFather и получить токен
• Как создать C#-проект и подключить Telegram.Bot
• Как принимать сообщения и постепенно собрать основу рабочего бота

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


➡️ C# Ready | #статья

Читать полностью…
Subscribe to a channel