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

Автор: @energy_c

РКН: https://clck.ru/3SBaT3

Реклама на бирже: https://telega.in/c/csharp_ready
Download Telegram
Как не сломать Dictionary в C# из-за регистра строк?

Очень частая ситуация: ключи приходят из API, заголовков, логинов, команд или конфигов. В одном месте строка может быть Admin, в другом admin.

Обычный Dictionary сравнивает строки с учётом регистра:
var roles = new Dictionary<string, int>();

roles["Admin"] = 10;

Console.WriteLine(roles.ContainsKey("admin"));


Результат будет:
False


Если регистр не должен иметь значения, это нужно указать при создании словаря:
var roles = new Dictionary<string, int>(
StringComparer.OrdinalIgnoreCase
);


Теперь ключи сравниваются без учёта регистра:
roles["Admin"] = 10;

Console.WriteLine(roles["admin"]);


Вывод:
10


Это полезно для HTTP-заголовков:
var headers = new Dictionary<string, string>(
StringComparer.OrdinalIgnoreCase
);


Для команд:
var commands = new Dictionary<string, Action>(
StringComparer.OrdinalIgnoreCase
);


И для конфигов, где пользователь может написать ключ в разном регистре:
settings["LogLevel"] = "Debug";
Console.WriteLine(settings["loglevel"]);


Лучше задавать comparer сразу при создании словаря. Приводить ключи вручную через ToLower() хуже: можно забыть сделать это в одном месте и получить странный баг.

➡️ C# Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
👍106🔥6
5👍3🔥1
This media is not supported in your browser
VIEW IN TELEGRAM
🐱 Awesome .NET — подборка полезных ресурсов для .NET-разработчиков!

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

Оставляю ссылочку: GitHub 📱


➡️ C# Ready | #репозиторий
Please open Telegram to view this post
VIEW IN TELEGRAM
9👍7🔥1
Интересная статья о том, как сделать нормальный онбординг в desktop-приложении на C# и Avalonia!

В этой статье:
• Как строятся интерактивные туры по интерфейсу с подсветкой элементов
• Почему desktop-приложениям не хватает готовых onboarding-инструментов
• Как автор решал позиционирование, скрытые элементы, темы и сохранение прогресса

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


➡️ C# Ready | #статья
Please open Telegram to view this post
VIEW IN TELEGRAM
4👍4🔥2
Почему в C# стоит аккуратно использовать DateTime.Now?

Когда нужно сохранить время события, часто пишут так:
var createdAt = DateTime.Now;


Для вывода пользователю это может быть нормально. Но для логики, хранения и сравнения времени DateTime.Now часто создаёт проблемы.

Он зависит от локального часового пояса машины:
Console.WriteLine(DateTime.Now);


На сервере, в контейнере, на CI и у разработчика время может отличаться.

Для технических событий обычно лучше брать UTC:
var createdAt = DateTime.UtcNow;


Такое значение проще хранить в базе, сравнивать и передавать между сервисами:
if (DateTime.UtcNow > token.ExpiresAtUtc) {
return false;
}


Но есть ещё один нюанс, DateTime сам по себе не хранит смещение часового пояса так явно, как DateTimeOffset.

Для событий, где важно зафиксировать момент времени, часто удобнее DateTimeOffset:
var createdAt = DateTimeOffset.UtcNow;


Например, для аудита:
var entry = new AuditEntry(
userId,
DateTimeOffset.UtcNow
);


А локальное время лучше получать только на границе приложения, перед показом пользователю, в отчёте или интерфейсе:
var local = createdAt.ToLocalTime();


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

➡️ C# Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
3👍2🔥1
This media is not supported in your browser
VIEW IN TELEGRAM
🧐 RefactoringGuru Design Patterns C# — паттерны проектирования на C#!

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

Оставляю ссылочку: GitHub 📱

➡️ C# Ready | #репозиторий
Please open Telegram to view this post
VIEW IN TELEGRAM
11👍7🔥4
Шпаргалка по LINQ в C#!

Например, Where() фильтрует коллекцию, Select() преобразует элементы, а GroupBy() собирает данные по ключу.

На картинке LINQ cheat sheet с примерами query syntax и lambda syntax: filtering, anonymous type, ordering, joining, grouping, paging через Skip/Take и базовые операции над последовательностями.

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

➡️ C# Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
12👍5🔥4
Собираем простой rate limiter для API на C#!

Иногда нужно ограничить частоту действий: попытки входа, запросы к endpoint-у, отправку писем или генерацию кодов. Сделаем маленький in-memory limiter, который разрешает не больше N действий за окно времени.

Создадим модель записи:
public sealed record Hit(
string Key,
DateTimeOffset CreatedAt
);


Хранить события будем в словаре:
private readonly Dictionary<string, Queue<DateTimeOffset>> _hits = new();
private readonly object _lock = new();


Метод проверки принимает ключ пользователя:
public bool Allow(string key, int limit, TimeSpan window)
{
var now = DateTimeOffset.UtcNow;
var border = now - window;


Очистим старые события:
    lock (_lock)
{
if (!_hits.TryGetValue(key, out var queue)) {
queue = new Queue<DateTimeOffset>();
_hits[key] = queue;
}

while (queue.Count > 0 && queue.Peek() < border) {
queue.Dequeue();
}


Если лимит уже достигнут, блокируем:
        if (queue.Count >= limit) {
return false;
}

queue.Enqueue(now);
return true;
}
}


Использование:
if (!limiter.Allow(userId, 5, TimeSpan.FromMinutes(1))) {
return Results.TooManyRequests();
}


Такой вариант подойдёт для одного процесса. Если приложение работает в нескольких инстансах, limiter лучше переносить в Redis или другой общий storage.

➡️ C# Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
7👍2🔥1
This media is not supported in your browser
VIEW IN TELEGRAM
The C# Academy большой сайт для прокачки C# и .NET через реальные проекты!

На сайте собрана практическая дорожная карта по C# и .NET: консольные приложения, базы данных, ASP.NET Core, Entity Framework, тестирование, API, fullstack-направление и проекты разной сложности. Фишка в том, что это не просто теория, а обучение через задачи и реальные мини-проекты: можно идти по роадмапу, закрывать темы, собирать портфолио и постепенно переходить от базового C# к нормальной backend-разработке на .NET.

Оставляю ссылочку: The C# Academy


➡️ C# Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
8🔥6👍4
Как в C# аккуратно ограничить область using?

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

Часто пишут так:
using var stream = File.OpenRead(path);

var hash = SHA256.HashData(stream);
DoSomethingElse();


using var освободит stream только в конце текущей области видимости. Если метод большой, файл может оставаться открытым дольше, чем нужно.

Для короткого ресурса лучше явно ограничить блок:
byte[] hash;

using (var stream = File.OpenRead(path))
{
hash = SHA256.HashData(stream);
}


После блока stream уже закрыт:
DoSomethingElse();


Это особенно важно для файлов:
using (var file = File.OpenWrite("report.txt"))
{
await file.WriteAsync(data);
}


И для сетевых или database-ресурсов, где лишнее удержание соединения может мешать другим операциям:
using (var connection = CreateConnection())
{
await connection.OpenAsync();
await SaveAsync(connection);
}


using var удобен, когда ресурс действительно нужен до конца метода:
using var activity = tracer.StartActivity("Import");


А обычный using (...) { } лучше, когда ресурс нужен только в маленьком блоке.

➡️ C# Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
9👍3🔥3
👍83🔥1
Делаем мини-router команд на C#!

Нужно обработать набор текстовых команд без длинного switch? В этой задаче собираем простой router на Dictionary, который находит нужный обработчик и не зависит от регистра введённой команды.

В этой задаче:
• Храним команды в Dictionary;
• Ищем обработчик через TryGetValue;
• Запускаем нужное действие.


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

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

LINQ часто выглядит как готовая коллекция:
var active = users.Where(u => u.IsActive);


Но на самом деле Where() возвращает отложенный запрос. Он не выполняется в момент создания, а запускается каждый раз, когда результат начинают перебирать.

Например:
var active = users.Where(u => {
Console.WriteLine(u.Name);
return u.IsActive;
});


Если пройтись по active два раза, фильтрация тоже выполнится два раза:
foreach (var user in active) { }

foreach (var user in active) { }


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

Когда результат нужен именно как снимок данных, лучше материализовать его явно:
var active = users
.Where(u => u.IsActive)
.ToList();


Теперь фильтрация выполнится один раз, а дальше код будет работать уже с готовым списком:
foreach (var user in active) { }
foreach (var user in active) { }


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

➡️ C# Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
👍107🔥3
This media is not supported in your browser
VIEW IN TELEGRAM
👍 TheAlgorithms C-Sharp — алгоритмы и структуры данных на C#!

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

Оставляю ссылочку: GitHub 📱

➡️ C# Ready | #репозиторий
Please open Telegram to view this post
VIEW IN TELEGRAM
👍116🔥6
Полезная статья про создание Telegram-бота на C# с нуля!

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

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


➡️ C# Ready | #статья
Please open Telegram to view this post
VIEW IN TELEGRAM
8👍5🔥3
Напоминалка по delegates, events и базовым конструкциям C#!

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

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

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

➡️ C# Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
👍95🔥5
Делаем простой троттлинг действий в 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 | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥75👍2