9818
Авторский канал по разработке на C# и Unity. Ресурсы, гайды, задачи, шпаргалки. Информация ежедневно пополняется! Автор: @energy_it РКН: https://clck.ru/3SBaT3 Реклама на бирже: https://telega.in/c/csharp_ready
👍 TheAlgorithms C-Sharp — алгоритмы и структуры данных на C#!
Этот репозиторий представляет собой большую коллекцию реализаций алгоритмов и структур данных на языке C#. Здесь можно найти примеры сортировок, поиска, работы со строками, математических алгоритмов, графов, деревьев и других классических задач из программирования.
Оставляю ссылочку: GitHub 📱
➡️ C# Ready | #репозиторий
Делаем мини-router команд на C#!
Нужно обработать набор текстовых команд без длинного switch? В этой задаче собираем простой router на Dictionary, который находит нужный обработчик и не зависит от регистра введённой команды.
В этой задаче:
• Храним команды в Dictionary;
• Ищем обработчик через TryGetValue;
• Запускаем нужное действие.
The C# Academy большой сайт для прокачки C# и .NET через реальные проекты!
На сайте собрана практическая дорожная карта по C# и .NET: консольные приложения, базы данных, ASP.NET Core, Entity Framework, тестирование, API, fullstack-направление и проекты разной сложности. Фишка в том, что это не просто теория, а обучение через задачи и реальные мини-проекты: можно идти по роадмапу, закрывать темы, собирать портфолио и постепенно переходить от базового C# к нормальной backend-разработке на .NET.
Оставляю ссылочку: The C# Academy
Шпаргалка по LINQ в C#!
Например, Where() фильтрует коллекцию, Select() преобразует элементы, а GroupBy() собирает данные по ключу.
На картинке LINQ cheat sheet с примерами query syntax и lambda syntax: filtering, anonymous type, ordering, joining, grouping, paging через Skip/Take и базовые операции над последовательностями.
Сохрани, чтобы не потерять!
➡️ C# Ready | #ресурс
Почему в C# стоит аккуратно использовать DateTime.Now?
Когда нужно сохранить время события, часто пишут так:
var createdAt = DateTime.Now;
Console.WriteLine(DateTime.Now);
var createdAt = DateTime.UtcNow;
if (DateTime.UtcNow > token.ExpiresAtUtc) {
return false;
}var createdAt = DateTimeOffset.UtcNow;
var entry = new AuditEntry(
userId,
DateTimeOffset.UtcNow
);
var local = createdAt.ToLocalTime();
🐱 Awesome .NET — подборка полезных ресурсов для .NET-разработчиков!
Этот репозиторий — большая коллекция материалов для работы с .NET. Здесь собраны полезные библиотеки, инструменты, фреймворки и проекты, которые помогают быстрее находить решения и лучше ориентироваться.
Оставляю ссылочку: GitHub 📱
Полезная статья о том, как сделать интеграционные тесты ASP.NET Core API чище и удобнее!
В этой статье:
• Как тестировать API вместе с базой, временем и внешними зависимостями
• Почему классический WebApplicationFactory быстро обрастает повторяющимся кодом
• Как фикстуры помогают собирать тестовое окружение декларативнее и проще
Продолжай читать на Habr!
Шпаргалка по возможностям C# 8!
Например, nullable reference types помогают явно отделять значения, которые могут быть null, а indices/ranges позволяют удобнее брать элементы и срезы через ^ и ...
На картинке default interface methods, nullable reference types, pattern matching, async streams и другие возможности с короткими примерами кода.
Сохрани, чтобы не потерять!
➡️ C# Ready | #ресурс
Шпаргалка по недооценённым LINQ-методам в C#!
Например, SequenceEqual() помогает сравнить две последовательности по элементам, ToLookup() создаёт удобную группировку для быстрого доступа по ключу, а GroupJoin() полезен, когда нужно связать две коллекции и получить вложенный результат.
На картинке 5 LINQ-методов с короткими примерами кода: SequenceEqual, Aggregate, GroupJoin, ToLookup и Intersect.
Сохрани, чтобы не потерять!
➡️ C# Ready | #ресурс
Почему yield return в C# выполняется не сразу?
Метод с yield выглядит как обычный метод:
IEnumerable<int> GetNumbers()
{
Console.WriteLine("start");
yield return 1;
yield return 2;
}
var numbers = GetNumbers();
Console.WriteLine("created");
created
foreach (var number in numbers)
{
Console.WriteLine(number);
}
start
1
2
var users = LoadUsers();
DoSomething();
foreach (var user in users)
{
Console.WriteLine(user.Name);
}
var users = LoadUsers().ToList();
Понятный разбор того, как на самом деле работает async/await в C#!
В этой статье:
• Чем асинхронность отличается от запуска работы в отдельном потоке
• Когда await освобождает поток, а когда выполнение продолжается синхронно
• Как компилятор превращает асинхронный метод в машину состояний
Продолжай читать на Habr!
👩💻 Сегодня рассмотрим Channel<T> — безопасная асинхронная очередь задач в .NET
Это потокобезопасная очередь для передачи сообщений между задачами без ручных блокировок и сложной синхронизации.
Что важно знать:
• Работает без ручных блокировок и сложных синхронных вызовов.
• Позволяет автоматически замедлять отправку при перегрузке памяти.
• Обеспечивает плавную передачу данных между параллельными задачами.
CancellationToken должен проходить через всю цепочку вызовов!
Само наличие CancellationToken в параметрах ещё не делает операцию отменяемой:
async Task SyncAsync(CancellationToken token)
{
var data = await LoadAsync();
await SaveAsync(data);
}
var data = await LoadAsync(token);
await SaveAsync(data, token);
await Task.Delay(500, token);
await запрос отмены необходимо проверять вручную:foreach (var item in items)
{
token.ThrowIfCancellationRequested();
Process(item);
}
Cancel() не останавливает код принудительно. Он только сообщает операциям, что работу следует завершить.using var timeout = new CancellationTokenSource(
TimeSpan.FromSeconds(10)
);
await SyncAsync(timeout.Token);
using var linked = CancellationTokenSource
.CreateLinkedTokenSource(token, timeout.Token);
await SyncAsync(linked.Token);
CancellationToken до самого нижнего отменяемого вызова, иначе API обещает отмену, но фактическая работа продолжается.
Интересный разбор того, как сегодня создавать игры на C# без готового движка!
В этой статье:
• Почему C# подходит для небольших кроссплатформенных игр
• Как SDL3 закрывает окна, ввод, контроллеры и рендеринг
• Как организовать загрузку ассетов и собрать собственный редактор на Dear ImGui
Продолжай читать на Habr!
Шпаргалка по форматированию даты и времени в C#!
Например, dd.MM.yyyy выведет дату с числом, месяцем и годом, HH:mm покажет время в 24-часовом формате, а MMM и MMMM позволяют получить сокращённое или полное название месяца.
На картинке спецификаторы для дня, месяца, года, часов, минут, секунд, долей секунды, часового пояса и AM/PM. Для каждого варианта приведены готовый формат и пример результата.
Сохрани, чтобы не потерять!
➡️ C# Ready | #ресурс
Почему LINQ-запрос может выполниться несколько раз?
LINQ часто выглядит как готовая коллекция:
var active = users.Where(u => u.IsActive);
var active = users.Where(u => {
Console.WriteLine(u.Name);
return u.IsActive;
});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) { }
Как в 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);
}
DoSomethingElse();
using (var file = File.OpenWrite("report.txt"))
{
await file.WriteAsync(data);
}using (var connection = CreateConnection())
{
await connection.OpenAsync();
await SaveAsync(connection);
}
using var удобен, когда ресурс действительно нужен до конца метода:using var activity = tracer.StartActivity("Import");using (...) { } лучше, когда ресурс нужен только в маленьком блоке.
Собираем простой 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();
}
🧐 RefactoringGuru Design Patterns C# — паттерны проектирования на C#!
Этот репозиторий поможет разобраться с основными паттернами проектирования и понять, как применять их в реальной разработке на C#. Здесь собраны примеры реализации популярных паттернов с понятным кодом и структурой, которая помогает быстро понять идею каждого решения.
Оставляю ссылочку: GitHub 📱
➡️ C# Ready | #репозиторий
Интересная статья о том, как сделать нормальный онбординг в desktop-приложении на C# и Avalonia!
В этой статье:
• Как строятся интерактивные туры по интерфейсу с подсветкой элементов
• Почему desktop-приложениям не хватает готовых onboarding-инструментов
• Как автор решал позиционирование, скрытые элементы, темы и сохранение прогресса
Продолжай читать на Habr!
Как не сломать 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
var headers = new Dictionary<string, string>(
StringComparer.OrdinalIgnoreCase
);
var commands = new Dictionary<string, Action>(
StringComparer.OrdinalIgnoreCase
);
settings["LogLevel"] = "Debug";
Console.WriteLine(settings["loglevel"]);
ToLower() хуже: можно забыть сделать это в одном месте и получить странный баг.
Собираем retry-обёртку для нестабильных операций!
В реальных приложениях операции иногда падают временно. В таких случаях полезно не сразу падать, а повторить попытку несколько раз.
Сделаем универсальный метод RetryAsync:
public static async Task<T> RetryAsync<T>(
Func<Task<T>> action,
int attempts,
TimeSpan delay
)
{
Exception? lastError = null;for (int i = 1; i <= attempts; i++)
{
try {
return await action();
}
catch (Exception ex) {
lastError = ex;
}
if (i < attempts) {
await Task.Delay(delay);
}
}throw lastError!;
}
var user = await RetryAsync(
() => api.GetUserAsync(id),
attempts: 3,
delay: TimeSpan.FromMilliseconds(300)
);
catch (Exception ex) {
lastError = ex;
Console.WriteLine($"Попытка {i} не удалась");
}public static async Task<T> RetryAsync<T>(
Func<CancellationToken, Task<T>> action,
int attempts,
TimeSpan delay,
CancellationToken token
)
await Task.Delay(delay, token);
Собираем простую очередь фоновых задач!
Иногда в приложении нужно отложить работу: отправить email, записать аудит, обработать файл или выполнить тяжёлую операцию после ответа пользователю.
Для этого можно собрать небольшую очередь на Channel<T>.
Сначала заведём интерфейс:
public interface ITaskQueue
{
ValueTask EnqueueAsync(Func<CancellationToken, Task> job);
IAsyncEnumerable<Func<CancellationToken, Task>> ReadAllAsync(
CancellationToken token
);
}
using System.Threading.Channels;
public sealed class TaskQueue : ITaskQueue
{
private readonly Channel<Func<CancellationToken, Task>> _channel =
Channel.CreateUnbounded<Func<CancellationToken, Task>>();
public ValueTask EnqueueAsync(Func<CancellationToken, Task> job)
{
return _channel.Writer.WriteAsync(job);
}
public IAsyncEnumerable<Func<CancellationToken, Task>> ReadAllAsync(
CancellationToken token
)
{
return _channel.Reader.ReadAllAsync(token);
}
}
await queue.EnqueueAsync(async token =>
{
await emailSender.SendAsync(user.Email, token);
});
await foreach (var job in queue.ReadAllAsync(token))
{
await job(token);
}
protected override async Task ExecuteAsync(CancellationToken token)
{
await foreach (var job in queue.ReadAllAsync(token))
{
await job(token);
}
}
Простая и популярная статья о создании Telegram-бота на C#!
В этой статье:
• Как создать бота через BotFather и подключить его к проекту
• Как принимать сообщения пользователя и отправлять ответы
• Как собрать базу, от которой уже можно развивать своего бота дальше
Продолжай читать на Habr!
🇷🇺 Разбираешься в радиочипах, оптике и связи?
Забери до 1 000 000 рублей за свои инженерные навыки на турнире «Дронкон» 🇷🇺
«Сталинские Соколы» открывают регистрацию на 4-й Всероссийский турнир «Дронкон», который пройдет с 22 по 26 августа.
Турнир пройдет по направлению:
- Инженерное дело: навыки программирования, сборка электронного оборудования, беспроводная связь, оптические системы + стратегия «Битва Дронов»;
Призовой фонд для победителей:
🥇место – 1 000 000 рублей
🥈место – 700 000 рублей
🥉место – 500 000 рублей
Награда за 4-8 места - 100 000 рублей
Пачку офферов заказывали?
О всех трудоустройствах через ИИ-ассистента Софи ребята узнают от пользователей.
И какой же кайф, когда они делятся своими историями.
Вот так в чате узнали сразу о трех новых офферах:
• Кейс Сергея - 2 недели в Софи и 2 оффера.
• Кейс Елены - 1 неделя в Софи и оффер спустя 10 лет перерыва в работе. Елене 52 года.
• Кейс Юлии - 1 месяц в Софи, свитчер из финансового аналитика в разработчика SQL. И еще один оффер🎉
Искренние поздравления ребятам и удачи им на новой работе!
В следующий раз доступ в Софи откроется уже 21 июля.
Если хочешь получить 3 дня бесплатного доступа - подписывайся на этот канал, все анонсы будут там.
«Снова в школу!» – звучит тревожно.
Но в магистратуре МИФИ и Школы 21 «Управление внедрением искусственного интеллекта в бизнес» совсем другая атмосфера: тут нет страха ошибки и ощущения, что зря тратишь время.
Зато есть рабочая среда, поддержка единомышленников и уверенность, что растёшь.
Что внутри программы:
— не просто алгоритмы, а живые сценарии – где применить ИИ, чтобы было лучше, быстрее, дешевле;
— учим внедрять ИИ для управления продуктом от технического задания до запуска;
— делаешь реальные проекты прямо во время учёбы, а не ближе к диплому;
— диплом государственного образца МИФИ – одного из ведущих технических университетов страны.
Подробности и заявка здесь
IT мероприятия 🤘
Всё самое интересное для карьеры в IT: митапы, хакатоны, конференции, стажировки и обучение.
Подписывайся 🤘
1. Москва
2. Санкт-Петербург
3. Для стажеров и студентов
Почему with у record не создаёт глубокую копию?
Выражение with удобно использовать для создания изменённой копии record:
var updated = user with
{
Name = "Alex"
};
record User(
string Name,
List<string> Roles
);
var original = new User(
"Alex",
new() { "read" }
);
var copy = original with { };
copy.Roles.Add("write");
Console.WriteLine(
original.Roles.Count
);
// 2
var copy = original with
{
Roles = new List<string>(
original.Roles
)
};
record User(
ImmutableArray<string> Roles
);
var copy = original with
{
Roles = original.Roles.Add("write")
};
Отправляем несколько HTTP-запросов параллельно на C#!
Если данные нужно получить из нескольких независимых источников, последовательные запросы заставят программу ждать каждый ответ по очереди.
Создадим HttpClient:
using var client = new HttpClient
{
Timeout = TimeSpan.FromSeconds(10)
};
Task<string> usersTask =
client.GetStringAsync(usersUrl);
Task<string> ordersTask =
client.GetStringAsync(ordersUrl);
Task<string> ratesTask =
client.GetStringAsync(ratesUrl);
await Task.WhenAll(
usersTask,
ordersTask,
ratesTask
);
string users = await usersTask;
string orders = await ordersTask;
string rates = await ratesTask;
using var cts =
new CancellationTokenSource(
TimeSpan.FromSeconds(5)
);
string data = await client.GetStringAsync(
usersUrl,
cts.Token
);