👶 Junior developer пишет:
🧑 Middle developer пишет:
🧓 Senior developer пишет:
🤔 Почему так происходит?
👉 Потому что булевы переменные — просты по сути, но часто используются неинтуитивно, что делает код запутанным и плохо поддерживаемым.
Вот 7 простых правил, как работать с булевыми значениями в C# и резко улучшить читаемость вашего кода:
1. 🚫 Избегайте двойных отрицаний
❌
✅
❌
✅
❌
✅
Двойные отрицания сбивают с толку и делают логику трудной для чтения.
2. ✅ Используйте понятные префиксы
✅
✅
✅
Префиксы мгновенно раскрывают назначение переменной.
3. 🔤 Предпочитайте единственное число
❌
✅
Единственное число делает значение ясным и конкретным.
4. 🎯 Используйте прилагательные для описания состояния
❌
✅
Булевы переменные должны описывать состояние, а не действие.
5. ⏱ Используйте настоящее время
❌
✅
Текущие состояния — проще и универсальнее в коде.
6. 🔁 Принцип раннего возврата (Return Early)
Пишите условия и булевы проверки так, чтобы код читался сверху вниз, как история — коротко, логично, понятно.
7. ⚠️ Не передавайте булевы параметры
❌
✅
Используйте перечисления или именованные константы — они понятны без документации.
📌 Вывод:
Хорошие имена булевых переменных — это не косметика, а основа читаемости и архитектурной чистоты.
Хорошо названный
💬 А ты какие правила используешь для наименования булевых переменных? Делись в комментариях 👇
notInactive
, tempBool
🧑 Middle developer пишет:
hadSubscriptionOnceUponATime
🧓 Senior developer пишет:
hasSubscription
, isActive
🤔 Почему так происходит?
👉 Потому что булевы переменные — просты по сути, но часто используются неинтуитивно, что делает код запутанным и плохо поддерживаемым.
Вот 7 простых правил, как работать с булевыми значениями в C# и резко улучшить читаемость вашего кода:
1. 🚫 Избегайте двойных отрицаний
❌
user.IsNotActive
✅
user.IsActive
❌
user.HasNoDept
✅
user.HasDept
❌
creditCard.IsNotExpired
✅
creditCard.IsExpired
Двойные отрицания сбивают с толку и делают логику трудной для чтения.
2. ✅ Используйте понятные префиксы
✅
Is
: user.IsActive
✅
Has
: user.HasDept
✅
Should
: order.ShouldBeCanceled
Префиксы мгновенно раскрывают назначение переменной.
3. 🔤 Предпочитайте единственное число
❌
areUsers
✅
hasUsers
Единственное число делает значение ясным и конкретным.
4. 🎯 Используйте прилагательные для описания состояния
❌
CancelOrder
✅
IsOrderCanceled
Булевы переменные должны описывать состояние, а не действие.
5. ⏱ Используйте настоящее время
❌
card.WasExpired
✅
card.IsExpired
Текущие состояния — проще и универсальнее в коде.
6. 🔁 Принцип раннего возврата (Return Early)
Пишите условия и булевы проверки так, чтобы код читался сверху вниз, как история — коротко, логично, понятно.
7. ⚠️ Не передавайте булевы параметры
❌
SendEmail(true)
✅
SendEmail(SendMode.Immediate)
Используйте перечисления или именованные константы — они понятны без документации.
📌 Вывод:
Хорошие имена булевых переменных — это не косметика, а основа читаемости и архитектурной чистоты.
Хорошо названный
isActive
понятнее, чем любые комментарии и тесты.💬 А ты какие правила используешь для наименования булевых переменных? Делись в комментариях 👇
[Best Practice] Явная конфигурация приложений ASP.NET Core
В большинстве существующих .NET-проектов конфигурация оказывается неочевидной, хотя в экосистеме .NET уже есть всё необходимое для ясной и понятной настройки ASP.NET Core веб-приложений с DI.
Как устроена конфигурация ASP.NET Core
- Источники (в порядке приоритета):
1. Host configuration
2.
3.
4. User secrets (в среде Development)
5. переменные окружения
6. аргументы командной строки
При дублировании ключей побеждает источник, указанный позже :contentReference[oaicite:0]{index=0}.
Выбор способа доступа к настройкам
- Через
- Через
Предпочтение паттерну Options даёт типобезопасный доступ к группам настроек и делает их использование очевидным :contentReference[oaicite:1]{index=1}.
Где хранить разные параметры
- `appsettings.json` — общие настройки (без секретов).
- `appsettings.{Environment}.json` — переопределения для конкретного окружения.
- User Secrets (`secrets.json`) — локальные секреты (пароли, ключи API); не используйте
- Azure Key Vault — производственные секреты через
- Переменные окружения и CLI — для срочных или разовых переопределений.
Рекомендации по внедрению
- Избегайте дублирования ключей — задавайте значение только там, где оно действительно меняется.
- В README укажите, что разработчикам нужно получить
- В IaC-скриптах чётко обозначьте необходимые секреты и параметры для каждого окружения.
Преимущества подхода
- Новым участникам проекта легко разобраться в конфигурации — каждый ключ встречается в одном месте.
- Возрастает поддерживаемость и прозрачность настроек.
- Упрощается автоматизация развёртывания (IaC): сразу видно, какие секреты и параметры требуются.
Читать
В большинстве существующих .NET-проектов конфигурация оказывается неочевидной, хотя в экосистеме .NET уже есть всё необходимое для ясной и понятной настройки ASP.NET Core веб-приложений с DI.
Как устроена конфигурация ASP.NET Core
- Источники (в порядке приоритета):
1. Host configuration
2.
appsettings.json
3.
appsettings.{Environment}.json
4. User secrets (в среде Development)
5. переменные окружения
6. аргументы командной строки
При дублировании ключей побеждает источник, указанный позже :contentReference[oaicite:0]{index=0}.
Выбор способа доступа к настройкам
- Через
IConfiguration
- Через
IOptions<T>
/ IOptionsMonitor<T>
Предпочтение паттерну Options даёт типобезопасный доступ к группам настроек и делает их использование очевидным :contentReference[oaicite:1]{index=1}.
Где хранить разные параметры
- `appsettings.json` — общие настройки (без секретов).
- `appsettings.{Environment}.json` — переопределения для конкретного окружения.
- User Secrets (`secrets.json`) — локальные секреты (пароли, ключи API); не используйте
appsettings.json
или appsettings.Development.json
для них :contentReference[oaicite:2]{index=2}. - Azure Key Vault — производственные секреты через
builder.Configuration.AddAzureKeyVault(...)
:contentReference[oaicite:3]{index=3}. - Переменные окружения и CLI — для срочных или разовых переопределений.
Рекомендации по внедрению
- Избегайте дублирования ключей — задавайте значение только там, где оно действительно меняется.
- В README укажите, что разработчикам нужно получить
secrets.json
из менеджера паролей и настроить User Secrets. - В IaC-скриптах чётко обозначьте необходимые секреты и параметры для каждого окружения.
Преимущества подхода
- Новым участникам проекта легко разобраться в конфигурации — каждый ключ встречается в одном месте.
- Возрастает поддерживаемость и прозрачность настроек.
- Упрощается автоматизация развёртывания (IaC): сразу видно, какие секреты и параметры требуются.
Читать
In-memory кэш в .NET: плюсы и минусы
✅ Плюсы:
• Очень быстрый
• Легко реализуется
• Не требует внешних зависимостей
⚠️ Минусы:
• Данные теряются при перезапуске
• Работает только в пределах одного инстанса
• Нет шаринга между приложениями
📌 Нужно больше — переходите на распределённый кэш.
Redis — топовый выбор, и в .NET есть два стабильных клиента:
•
•
📘 Хотите понять, когда и как использовать оба подхода?
Вот полный гайд:
https://milanjovanovic.tech/blog/caching-in-aspnetcore-improving-application-performance
✅ Плюсы:
• Очень быстрый
• Легко реализуется
• Не требует внешних зависимостей
⚠️ Минусы:
• Данные теряются при перезапуске
• Работает только в пределах одного инстанса
• Нет шаринга между приложениями
📌 Нужно больше — переходите на распределённый кэш.
Redis — топовый выбор, и в .NET есть два стабильных клиента:
•
StackExchange.Redis
•
Microsoft.Extensions.Caching.StackExchangeRedis
📘 Хотите понять, когда и как использовать оба подхода?
Вот полный гайд:
https://milanjovanovic.tech/blog/caching-in-aspnetcore-improving-application-performance
Дан код:
using System;
using System.Runtime.ExceptionServices;
class Program
{
static void Main()
{
try
{
Console.WriteLine(Foo());
}
catch (Exception ex)
{
Console.WriteLine($"Catch: {ex.Message}");
}
}
static int Foo()
{
try
{
return Bar();
}
finally
{
throw new Exception("finally in Foo");
}
}
static int Bar()
{
try
{
throw new Exception("error in Bar");
}
finally
{
Console.WriteLine("Bar finally executed");
}
}
}
Вопросы:
• Что напечатает программа и почему?
• В каком порядке выполняются блоки try / finally?
• Какая из двух ошибок дойдёт до catch, а какая потеряется?
• Как изменить код, чтобы не терять информацию о первой ошибке?
Разбор:
• Bar бросает Exception("error in Bar"). Прежде чем исключение покинет метод, выполняется его finally, выводя Bar finally executed.
• Управление переходит в Foo. Там срабатывает finally, который бросает новое исключение ("finally in Foo").
• По правилам CLR новое исключение из finally заменяет текущее. Первая ошибка пропадает.
• В Main перехватывается только «finally in Foo».
Консольный вывод:
Bar finally executed
Catch: finally in Foo
Как сохранить обе ошибки
Обернуть второе исключение так, чтобы первая причина не потерялась, например:
finally
{
var pending = new Exception("finally in Foo");
throw new AggregateException("Foo failed", pending);
}
или переслать исходное исключение через ExceptionDispatchInfo:
Exception pending = null;
static int Foo()
{
try
{
return Bar();
}
catch (Exception ex)
{
pending = ex; // сохраняем первую причину
throw;
}
finally
{
if (pending != null)
ExceptionDispatchInfo.Capture(pending).Throw();
}
}```
Так ни одна ошибка не будет потеряна, а отладка станет нагляднее.
Please open Telegram to view this post
VIEW IN TELEGRAM
Проблема: cортировка больших массивов может быть неэффективной при использовании простых алгоритмов, таких как сортировка пузырьком или вставками.
Решение: Автор в книге Algorithms and Data Structures for OOP With C# демонстрирует реализацию QuickSort — одного из самых эффективных алгоритмов сортировки на практике, с рекурсивным разбиением массива.
Пример кода:
public class QuickSortExample
{
public void QuickSort(int[] arr, int low, int high)
{
if (low < high)
{
int pi = Partition(arr, low, high);
QuickSort(arr, low, pi - 1);
QuickSort(arr, pi + 1, high);
}
}
private int Partition(int[] arr, int low, int high)
{
int pivot = arr[high];
int i = (low - 1);
for (int j = low; j < high; j++)
{
if (arr[j] < pivot)
{
i++;
(arr[i], arr[j]) = (arr[j], arr[i]);
}
}
(arr[i + 1], arr[high]) = (arr[high], arr[i + 1]);
return i + 1;
}
}
Преимущества:
— Быстрая сортировка даже больших наборов данных
— Средняя сложность O(n log n)
— Эффективное использование памяти за счет рекурсии
Please open Telegram to view this post
VIEW IN TELEGRAM
Трюк: добавление элементов в словарь со списками через `??=`
Разбор:
- При обращении к dict[key] отсутствующий ключ вернёт default(List<int>), то есть null.
- Оператор ??= проверяет левую часть на null и, если она null, присваивает справа новое значение.
- В нашем случае, если dict[key] был null, создаётся новый List<int> и сразу сохраняется в словаре.
- После этого метод .Add(item) вызывается уже на существующем списке.
- В результате за одну строчку мы и проверили наличие, и создали новый список при необходимости, и добавили элемент.
var dict = new Dictionary<string, List<int>>();
string key = "numbers";
int item = 42;
// Обычный способ:
if (!dict.TryGetValue(key, out var list))
{
list = new List<int>();
dict[key] = list;
}
list.Add(item);
// Короткий трюк:
(dict[key] ??= new List<int>()).Add(item);
Разбор:
- При обращении к dict[key] отсутствующий ключ вернёт default(List<int>), то есть null.
- Оператор ??= проверяет левую часть на null и, если она null, присваивает справа новое значение.
- В нашем случае, если dict[key] был null, создаётся новый List<int> и сразу сохраняется в словаре.
- После этого метод .Add(item) вызывается уже на существующем списке.
- В результате за одну строчку мы и проверили наличие, и создали новый список при необходимости, и добавили элемент.
🎯 Using .NET Aspire With the Docker Publisher — практическое руководство от Milan Jovanović
.NET Aspire — это современный фреймворк от Microsoft для создания облачных микросервисов. В статье показано, как автоматически интегрировать Docker Compose в .NET‑приложение с помощью нового инструмента — Aspire Docker Publisher.
✅ Что вы узнаете:
1. Как описать окружение прямо в C#:
2. Как опубликовать проект:
После чего автоматически создаётся
3. Что входит в результат:
- Готовый
- Поддержка портов, переменных среды, volume и сетей
- Полная инфраструктура, которую можно деплоить хоть на VPS
4. Как это работает на проде:
- Всё, что нужно: скопировать артефакты →
- Можно легко обернуть через Nginx или Traefik, подключить SSL
🧠 Почему это удобно:
- Не нужно вручную писать YAML — всё в коде
- Повышается воспроизводимость и читаемость инфраструктуры
- Упрощает переход от локальной разработки к боевому деплою
🔗 Статья: www.milanjovanovic.tech/blog/using-dotnet-aspire-with-the-docker-publisher
.NET Aspire — это современный фреймворк от Microsoft для создания облачных микросервисов. В статье показано, как автоматически интегрировать Docker Compose в .NET‑приложение с помощью нового инструмента — Aspire Docker Publisher.
✅ Что вы узнаете:
1. Как описать окружение прямо в C#:
builder.AddDockerComposeEnvironment("aspire-docker-demo");
var postgres = builder.AddPostgres("database").WithDataVolume();
var redis = builder.AddRedis("cache");
var webApi = builder.AddProject<Projects.Web_Api>("web-api")
.WithReference(postgres).WaitFor(postgres)
.WithReference(redis).WaitFor(redis);
builder.Build().Run();
2. Как опубликовать проект:
dotnet tool install --global aspire.cli --prerelease
aspire publish -o docker-compose-artifacts
После чего автоматически создаётся
docker-compose.yml
и .env
.3. Что входит в результат:
- Готовый
docker-compose.yml
со всеми зависимостями- Поддержка портов, переменных среды, volume и сетей
- Полная инфраструктура, которую можно деплоить хоть на VPS
4. Как это работает на проде:
- Всё, что нужно: скопировать артефакты →
docker compose up -d
- Можно легко обернуть через Nginx или Traefik, подключить SSL
🧠 Почему это удобно:
- Не нужно вручную писать YAML — всё в коде
- Повышается воспроизводимость и читаемость инфраструктуры
- Упрощает переход от локальной разработки к боевому деплою
🔗 Статья: www.milanjovanovic.tech/blog/using-dotnet-aspire-with-the-docker-publisher
🧠 .NET-задача для продвинутых: потокобезопасная очередь с приоритетами и отменой задач
Задача:
Реализуйте асинхронный планировщик задач с приоритетами и возможностью отмены. Требования:
1. Есть очередь задач (`Func<CancellationToken, Task>`), каждая с целочисленным приоритетом (0 — самый высокий).
2. Задачи выполняются параллельно, но не более
3. Если приходит задача с более высоким приоритетом, и нет свободных слотов, она может вытеснить задачу с самым низким приоритетом.
4. Вытеснённая задача должна быть отменена (через `CancellationToken`), и её ресурсы — корректно освобождены.
5. Код должен быть потокобезопасным и устойчивым к гонкам.
Дополнительно:
- Используйте
- Не допускайте deadlock’ов
- Обеспечьте корректное завершение планировщика по команде
Пример API:
@csharp_1001_notes
Задача:
Реализуйте асинхронный планировщик задач с приоритетами и возможностью отмены. Требования:
1. Есть очередь задач (`Func<CancellationToken, Task>`), каждая с целочисленным приоритетом (0 — самый высокий).
2. Задачи выполняются параллельно, но не более
N
одновременно.3. Если приходит задача с более высоким приоритетом, и нет свободных слотов, она может вытеснить задачу с самым низким приоритетом.
4. Вытеснённая задача должна быть отменена (через `CancellationToken`), и её ресурсы — корректно освобождены.
5. Код должен быть потокобезопасным и устойчивым к гонкам.
Дополнительно:
- Используйте
PriorityQueue
, SemaphoreSlim
, CancellationTokenSource
- Не допускайте deadlock’ов
- Обеспечьте корректное завершение планировщика по команде
StopAsync()
Пример API:
public class PriorityTaskScheduler
{
public PriorityTaskScheduler(int maxParallelism);
public Task EnqueueAsync(Func<CancellationToken, Task> task, int priority);
public Task StopAsync();
}
@csharp_1001_notes
⚠️ Как вы обрабатываете ошибки в C#?
Многие используют исключения для управления потоком и быстрого фейла. Но в C# метод не сообщает, какие именно исключения он может выбросить — это не видно в сигнатуре.
🔍 Мой подход:
Исключения — только для исключительных ситуаций.
Если метод может ожидаемо провалиться, пусть это будет явно.
✅ Используйте Result-паттерн:
— Метод возвращает
— Caller обязан проверить
— Код становится предсказуемее и легче тестируется
— Дополнительно: пропускная способность может быть выше, чем при
Пример:
Подробнее
Многие используют исключения для управления потоком и быстрого фейла. Но в C# метод не сообщает, какие именно исключения он может выбросить — это не видно в сигнатуре.
🔍 Мой подход:
Исключения — только для исключительных ситуаций.
Если метод может ожидаемо провалиться, пусть это будет явно.
✅ Используйте Result-паттерн:
— Метод возвращает
Result<T>
вместо выбрасывания исключения — Caller обязан проверить
IsSuccess
и обработать ошибку — Код становится предсказуемее и легче тестируется
— Дополнительно: пропускная способность может быть выше, чем при
throw/catch
Пример:
Result<User> result = userService.FindById(id);
if (!result.IsSuccess)
return Error(result.Error);
Подробнее
Если ты пишешь на .NET — возможно, ты хотя бы раз совершал одну из этих архитектурных (или просто утомительных) ошибок. Вот список анти-паттернов.
1. Blazor вместо React
Переизобретать веб на C# ради UI? Ты не Google. Если нужен зрелый фронт, бери то, что уже доказало свою масштабируемость.
2. Serverless Azure Functions на одном App Service Plan
"О, это же серверлесс!" — пока не замечаешь, что всё сидит на одном сервисе. Масштабируемость мнимая, расходы настоящие.
3. Самодельный MVC поверх Minimal APIs
Зачем ты пишешь свою обвязку маршрутов, контроллеров и зависимостей, если MVC уже всё это умеет?
4. Хранимки для CRUD
Хранимки — не антипаттерн, но когда их 200 штук для банального Insert/Update/Delete — это просто ORM с человеческим лицом, только сложнее.
5. "Юнит-тесты", которым нужен деплой БД
Это не юнит-тесты. Это интеграционные, а если быть честным — "fragile-тесты".
6. AutoMapper
В теории красиво, в проде — «а почему здесь null?». Иногда проще написать руками и понять, что происходит.
7. Игнор Microsoft Orleans
Если делаешь real-time, pub/sub, воркеры — Orleans стоит хотя бы попробовать. Он закрывает много боли, которую ты иначе сам будешь собирать по кускам.
8. Начинать с "микросервисов"
Монолит — не зло. Зло — это 12 проектов в solution, которые общаются через HTTP и собираются 10 минут.
9. gRPC для фронта
Если у тебя браузер → JS → gRPC → base64 → JSON — просто остановись. Возьми REST или GraphQL и живи спокойно.
10. Razor Pages вместо MVC
Да, они проще на старте. Но когда проект растёт, ты захочешь нормальное разделение по слоям, маршрутам и структуре.
🧠 Вывод: не все возможности .NET надо использовать. Иногда сила — в простоте.
C чем согласны ? Что добавили бы ?
#dotnet #csharp #webdev #архитектура #программирование
Please open Telegram to view this post
VIEW IN TELEGRAM
🔐 Блог DevelopersVoice выпустил отличный гайд по **10 главным уязвимостям веб‑приложений с примерами на .NET.
Что внутри:
• Инъекции и XSS
• Ошибки аутентификации
• Уязвимые зависимости
• SSRF и плохая конфигурация
• Проблемы с логированием и безопасным дизайном
📌 Всё с практическими советами: как обнаружить, как исправить, как не допустить.
Полный гайд тут: https://developersvoice.com/blog/secure-coding/owasp-top-ten
#OWASP #SecureCoding #DotNet #WebSecurity #DevTips
Что внутри:
• Инъекции и XSS
• Ошибки аутентификации
• Уязвимые зависимости
• SSRF и плохая конфигурация
• Проблемы с логированием и безопасным дизайном
📌 Всё с практическими советами: как обнаружить, как исправить, как не допустить.
Полный гайд тут: https://developersvoice.com/blog/secure-coding/owasp-top-ten
#OWASP #SecureCoding #DotNet #WebSecurity #DevTips
📌 PolySharp — удобный способ использовать новые фичи C# на старых версиях .NET. Этот NuGet-пакет работает как source-генератор, автоматически подбирая нужные полифиллы в зависимости от целевой платформы. Для работы достаточно добавить ссылку на PolySharp, установить последнюю версию C# и можно писать современный код даже для .NET Framework или UWP.
Инструмент обладает умной генерацией только необходимых типов. Например, если компилятору C# 13 нужен
🤖 GitHub
@csharp_ci
Инструмент обладает умной генерацией только необходимых типов. Например, если компилятору C# 13 нужен
[IsExternalInit]
для init-only свойств, PolySharp создаст его за кулисами. При этом он не трогает фичи, требующие поддержки рантайма, но покрывает огромный пласт синтаксических улучшений — от nullable-аннотаций до интерполированных строковых обработчиков. 🤖 GitHub
@csharp_ci
Какое исключение выдается, если протокол, поддерживаемый префиксом URI, недействителен?
Anonymous Quiz
10%
URLNotFound
29%
NotSupportedException
48%
UriFormatException
12%
URLSourceNotFound
🔥 .NET Aspire: как упростить Service Discovery в микросервисах
Вместо ручной настройки адресов и портов, фреймворк .NET Aspire позволяет упростить обнаружение сервисов с помощью декларативного подхода.
🔹 Конфигурация через
🔹 Сервисы автоматически "обнаруживаются" и подключаются
🔹 Поддержка локальной разработки, контейнеров и облака
🔹 Логи, метрики, health checks — встроены
📌 Aspire — это не просто упрощение разработки, это фундамент для масштабируемых .NET-приложений.
🧠 Полный разбор — в блоге:
[how-dotnet-aspire-simplifies-service-discovery](https://www.milanjovanovic.tech/blog/how-dotnet-aspire-simplifies-service-discovery)
Подпишись, чтобы не пропускать важные новинки .NET и облачной разработки.
Вместо ручной настройки адресов и портов, фреймворк .NET Aspire позволяет упростить обнаружение сервисов с помощью декларативного подхода.
🔹 Конфигурация через
.AddProject()
и .WithReference()
🔹 Сервисы автоматически "обнаруживаются" и подключаются
🔹 Поддержка локальной разработки, контейнеров и облака
🔹 Логи, метрики, health checks — встроены
📌 Aspire — это не просто упрощение разработки, это фундамент для масштабируемых .NET-приложений.
🧠 Полный разбор — в блоге:
[how-dotnet-aspire-simplifies-service-discovery](https://www.milanjovanovic.tech/blog/how-dotnet-aspire-simplifies-service-discovery)
Подпишись, чтобы не пропускать важные новинки .NET и облачной разработки.
😈 Хитрая задачка на C# — замыкания и ловушка в цикле
Что выведет этот код?
На первый взгляд кажется, что будет:
Но на самом деле вывод:
💡 Почему?
Все лямбды замкнулись на одну и ту же переменную i, и когда они выполняются — i уже стало 5.
✅ Как исправить:
Теперь всё работает как ожидается.
🧠 Замыкания в C# захватывают переменные, а не их значения! Аккуратнее с циклами и лямбдами.
Что выведет этот код?
var actions = new List<Action>();
for (int i = 0; i < 5; i++)
{
actions.Add(() => Console.WriteLine(i));
}
foreach (var action in actions)
{
action();
}
На первый взгляд кажется, что будет:
0
1
2
3
4
Но на самом деле вывод:
5
5
5
5
5
💡 Почему?
Все лямбды замкнулись на одну и ту же переменную i, и когда они выполняются — i уже стало 5.
✅ Как исправить:
for (int i = 0; i < 5; i++)
{
int copy = i;
actions.Add(() => Console.WriteLine(copy));
}
Теперь всё работает как ожидается.
🧠 Замыкания в C# захватывают переменные, а не их значения! Аккуратнее с циклами и лямбдами.
Почти каждый разработчик допускал эту ошибку.
Что не так с этим кодом?
На первый взгляд всё кажется логичным:
🔸 API-эндпоинт регистрации пользователя вызывает
🔸
🔸 EmailService через
Но если присмотреться, метод
В чём проблема с
Вот суть:
❌
Если внутри
Вместо этого приложение может тихо упасть или начать вести себя непредсказуемо.
Почему так происходит?
Методы
Исключения из async void проходят мимо стандартных механизмов обработки.
Правильный подход:
✅ Всегда возвращай Task
Запомни:
Что не так с этим кодом?
На первый взгляд всё кажется логичным:
UserService
UserService
сохраняет пользователя в базу и вызывает EmailService
SmtpClient
отправляет письмоНо если присмотреться, метод
SendWelcomeEmail
объявлен как async void
.В чём проблема с
async void
?Вот суть:
async void
делает невозможным отлов исключений.Если внутри
SendEmailAsync()
произойдёт исключение — catch
его не перехватит.Вместо этого приложение может тихо упасть или начать вести себя непредсказуемо.
Почему так происходит?
Методы
async void
не возвращают Task, поэтому вызывающий код не может их await-ить и обрабатывать ошибки.Исключения из async void проходят мимо стандартных механизмов обработки.
Правильный подход:
Запомни:
async void
допустим только для обработчиков событий, где возвращаемый void обязателен.Please open Telegram to view this post
VIEW IN TELEGRAM
Рефлексия тормозит приложения? Пора управлять компилятором!
Устали писать одинаковый код снова и снова? Рефлексия съедает производительность в рантайме, а генерация boilerplate-кода отнимает часы. Современный C# предлагает элегантное решение — заставить компилятор работать на вас.
🎥 Вебинар «Заставьте компилятор работать на вас: магия метапрограммирования с Source Generators в C#»
📅 6 августа, 20:00
👩🏫 Елена Сычева
Разберём на практике:
→ Почему рефлексия — узкое место и как Source Generators решают проблему на этапе компиляции
→ Устройство компилятора Roslyn: синтаксические деревья и семантическая модель
→ Live-coding первого Source Generator для избавления от рутинного кода
→ Применение генераторов в популярных библиотеках .NET 7+
Результат: вы научитесь создавать собственные генераторы кода, которые автоматизируют рутину и повышают производительность приложений без runtime-накладных расходов.
Вебинар проходит в рамках курса «C# углубленный» — участники получают эксклюзивные условия обучения.
→ Регистрация: https://otus.pw/X1GN/
Реклама. ООО "ОТУС ОНЛАЙН-ОБРАЗОВАНИЕ", ИНН: 9705100963
Устали писать одинаковый код снова и снова? Рефлексия съедает производительность в рантайме, а генерация boilerplate-кода отнимает часы. Современный C# предлагает элегантное решение — заставить компилятор работать на вас.
🎥 Вебинар «Заставьте компилятор работать на вас: магия метапрограммирования с Source Generators в C#»
📅 6 августа, 20:00
👩🏫 Елена Сычева
Разберём на практике:
→ Почему рефлексия — узкое место и как Source Generators решают проблему на этапе компиляции
→ Устройство компилятора Roslyn: синтаксические деревья и семантическая модель
→ Live-coding первого Source Generator для избавления от рутинного кода
→ Применение генераторов в популярных библиотеках .NET 7+
Результат: вы научитесь создавать собственные генераторы кода, которые автоматизируют рутину и повышают производительность приложений без runtime-накладных расходов.
Вебинар проходит в рамках курса «C# углубленный» — участники получают эксклюзивные условия обучения.
→ Регистрация: https://otus.pw/X1GN/
Реклама. ООО "ОТУС ОНЛАЙН-ОБРАЗОВАНИЕ", ИНН: 9705100963
Правильный способ построения асинхронных API в .NET
Большинство API работают по простому шаблону:
🔸 Клиент отправляет запрос
🔸 Сервер выполняет работу
🔸 Сервер возвращает ответ
Такой подход отлично работает для быстрых операций — например, получения данных или простых обновлений.
А что насчёт длительных операций?
Речь о задачах вроде:
> обработки больших файлов
> генерации отчётов
> конвертации видео
Такие процессы могут занимать от нескольких минут до часов.
Вот как можно правильно строить асинхронные API: читать
Как сообщить клиенту, что его запрос обработан?
Есть два подхода:
🔸 PULL — клиент опрашивает API, чтобы узнать статус
🔸 PUSH — сервер сам уведомляет клиента (через WebSocket, email и т.д.)
Большинство API работают по простому шаблону:
Такой подход отлично работает для быстрых операций — например, получения данных или простых обновлений.
А что насчёт длительных операций?
Речь о задачах вроде:
> обработки больших файлов
> генерации отчётов
> конвертации видео
Такие процессы могут занимать от нескольких минут до часов.
Вот как можно правильно строить асинхронные API: читать
Как сообщить клиенту, что его запрос обработан?
Есть два подхода:
Please open Telegram to view this post
VIEW IN TELEGRAM
Хотите разобраться в тестировании API на ASP.NET Core?
➡️ Присоединяйтесь к открытому уроку «Тестирование API в ASP.NET Core: Интеграция и Нагрузка» 5 августа в 20:00 МСК. На вебинаре мы:
- Разберем ключевые концепции интеграционного и нагрузочного тестирования.
- Научимся писать интеграционные тесты с популярными библиотеками.
- Освоим инструменты для нагрузочного тестирования и анализа результатов.
📗 Этот урок — отличная подготовка к курсу «C# ASP.NET Core разработчик», который стартует совсем скоро. Все участники вебинара получат скидку на обучение!
Записаться на вебинар → https://otus.pw/sqqO/?erid=2W5zFHob1Sg
Реклама. ООО "ОТУС ОНЛАЙН-ОБРАЗОВАНИЕ". ИНН 9705100963.
➡️ Присоединяйтесь к открытому уроку «Тестирование API в ASP.NET Core: Интеграция и Нагрузка» 5 августа в 20:00 МСК. На вебинаре мы:
- Разберем ключевые концепции интеграционного и нагрузочного тестирования.
- Научимся писать интеграционные тесты с популярными библиотеками.
- Освоим инструменты для нагрузочного тестирования и анализа результатов.
📗 Этот урок — отличная подготовка к курсу «C# ASP.NET Core разработчик», который стартует совсем скоро. Все участники вебинара получат скидку на обучение!
Записаться на вебинар → https://otus.pw/sqqO/?erid=2W5zFHob1Sg
Реклама. ООО "ОТУС ОНЛАЙН-ОБРАЗОВАНИЕ". ИНН 9705100963.
🧠 Задача: "Сколько раз выполнится блок Console.WriteLine?"
❓Вопрос:
Что выведет программа?
🔍 Разбор:
На первый взгляд кажется, что программа выведет:
1
2
3
📎 Но на самом деле она выведет:
0
1
2
Почему?
Оператор obj++ — это постфиксный инкремент. Он:
сначала вызывает implicit operator int для текущего значения,
а затем вызывает operator ++.
То есть порядок такой:
Console.WriteLine(obj++) вызывает implicit int до инкремента
только потом ++ увеличивает значение.
✅ Что это проверяет:
Знание порядка вызова операторов (++ и implicit)
Понимание поведения постфиксных операций в .NET
Умение читать и анализировать перегрузки операторов
@csharp_1001_notes
public class Program
{
public static void Main()
{
var obj = new Counter();
for (int i = 0; i < 3; i++)
{
Console.WriteLine(obj++);
}
}
}
public class Counter
{
private int _value = 0;
public static Counter operator ++(Counter c)
{
c._value++;
return c;
}
public static implicit operator int(Counter c)
{
return c._value;
}
}
❓Вопрос:
Что выведет программа?
🔍 Разбор:
1
2
3
📎 Но на самом деле она выведет:
0
1
2
Почему?
Оператор obj++ — это постфиксный инкремент. Он:
сначала вызывает implicit operator int для текущего значения,
а затем вызывает operator ++.
То есть порядок такой:
Console.WriteLine(obj++) вызывает implicit int до инкремента
только потом ++ увеличивает значение.
✅ Что это проверяет:
Знание порядка вызова операторов (++ и implicit)
Понимание поведения постфиксных операций в .NET
Умение читать и анализировать перегрузки операторов
@csharp_1001_notes