C# 1001 notes
6.53K subscribers
319 photos
9 videos
2 files
306 links
Регулярные короткие заметки по C# и .NET.

Просто о сложном для каждого.

admin - @haarrp
Download Telegram
⚡️ Если ты запускаешь десяток терминалов для старта приложения

С .NET Aspire можно запускать backend, frontend и все зависимости одновременно — быстро и удобно!

Смотри, как это сделать: justkeepcoding.tech/blog/integrating-angular-into-dotnet-aspire
🗂️ Entity Framework Core — ORM нового поколения для работы с БД. Этот инструмент от Microsoft кардинально меняет подход к взаимодействию с реляционными и NoSQL базами данных.

Для работы инструмент предоставляет мощный слой абстракции, позволяя работать с данными как с объектами, автоматизируя CRUD-операции и миграции схемы. EF Core поддерживает широкий пласт СУБД: от классических SQL Server и PostgreSQL до документоориентированной Cosmos DB. Для SQLite-разработчиков есть отдельный оптимизированный провайдер Microsoft.Data.Sqlite.

🤖 GitHub
Forwarded from C++ Academy
This media is not supported in your browser
VIEW IN TELEGRAM
🚀 Разработчик показал движок на C, который работает в 14 раз быстрее Unity в браузере — и теперь доступна онлайн-демо.

Многие не поверили в заявленную разницу в производительности, поэтому он выложил демо в открытый доступ. Сравнение проводится с реальным Unity-проектом, выложенным на GitHub.

🛠️ C-движок демонстрирует:
• Существенно более высокую FPS в браузере
• Минимальную просадку при рендеринге
• Низкий overhead по сравнению с WebAssembly-сборкой Unity

💬 Автор пока не решил, выкладывать ли исходники C-версии — рассматривает вариант лицензии CC (non-commercial).



🔗 Демо: https://cgamedev.com/
🔗 Код: https://github.com/gabrieldechichi/unity_webglperftest


@cpluspluc
👶 Junior developer пишет: 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. 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 есть два стабильных клиента:
StackExchange.Redis
Microsoft.Extensions.Caching.StackExchangeRedis

📘 Хотите понять, когда и как использовать оба подхода?
Вот полный гайд:
https://milanjovanovic.tech/blog/caching-in-aspnetcore-improving-application-performance
🖥 Задача «Перехват исключений и потеря контекста» (C# 12)

Дан код:


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
🖥Быстрая сортировка (QuickSort) с использованием рекурсии

Проблема: 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
Трюк: добавление элементов в словарь со списками через `??=`


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#:

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. Задачи выполняются параллельно, но не более 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#: за гранью await. Паттерны, ошибки и оптимизация для профессионалов».

🗓 17 июля в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «C# Developer. Professional».

Что будет на вебинаре:
✔️ Разбор сложных сценариев: цепочки задач, параллельный async, комбинирование с параллелизмом (Task, Parallel, async).
✔️ Распространенные ошибки (deadlocks, async void, контексты синхронизации) и как их точно избегать.
✔️ Паттерны: CancellationTokens, ValueTask, IAsyncDisposable, кастомные awaiterы (обзорно).
✔️ Когда и как измерять производительность async кода, что может стать узким местом.
✔️ Best practices для реальных высоконагруженных сценариев.

Кому будет полезно:
- Разработчикам C# с опытом (Mid+/Senior), которые активно используют async/await.
- Тем, кто сталкивался с непонятными блокировками или проблемами производительности в асинхронном коде.
- Тем, кто хочет писать более надежный и эффективный асинхронный код.

🔗 Ссылка на регистрацию: https://otus.pw/SIHc/
⚠️ Как вы обрабатываете ошибки в C#?

Многие используют исключения для управления потоком и быстрого фейла. Но в C# метод не сообщает, какие именно исключения он может выбросить — это не видно в сигнатуре.

🔍 Мой подход:
Исключения — только для исключительных ситуаций.

Если метод может ожидаемо провалиться, пусть это будет явно.

Используйте Result-паттерн:

— Метод возвращает Result<T> вместо выбрасывания исключения
— Caller обязан проверить IsSuccess и обработать ошибку
— Код становится предсказуемее и легче тестируется
— Дополнительно: пропускная способность может быть выше, чем при throw/catch

Пример:

Result<User> result = userService.FindById(id);
if (!result.IsSuccess)
return Error(result.Error);


Подробнее
🖥 Топ-10 ошибок .NET‑разработчиков

Если ты пишешь на .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
📌 PolySharp — удобный способ использовать новые фичи C# на старых версиях .NET. Этот NuGet-пакет работает как source-генератор, автоматически подбирая нужные полифиллы в зависимости от целевой платформы. Для работы достаточно добавить ссылку на PolySharp, установить последнюю версию C# и можно писать современный код даже для .NET Framework или UWP.

Инструмент обладает умной генерацией только необходимых типов. Например, если компилятору C# 13 нужен [IsExternalInit] для init-only свойств, PolySharp создаст его за кулисами. При этом он не трогает фичи, требующие поддержки рантайма, но покрывает огромный пласт синтаксических улучшений — от nullable-аннотаций до интерполированных строковых обработчиков.

🤖 GitHub

@csharp_ci
Какое исключение выдается, если протокол, поддерживаемый префиксом URI, недействителен?
Anonymous Quiz
11%
URLNotFound
30%
NotSupportedException
48%
UriFormatException
11%
URLSourceNotFound
🔥 .NET Aspire: как упростить Service Discovery в микросервисах

Вместо ручной настройки адресов и портов, фреймворк .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 и облачной разработки.
😱💻  Хотите прокачать архитектурные скилы и стать востребованным fullstack-разработчиком?

Пройдите вступительный тест и получите бесплатные уроки курса «C# ASP.NET Core разработчик» от OTUS!

👉 Пройти тест: https://otus.pw/gmbX/

🚀 Зарядите карьеру: увеличьте доход, берите сложные проекты и работайте с современным стеком!
Всего за 6 месяцев вы научитесь:
• Разрабатывать веб-приложения на ASP.NET Core, рассматривая ASP.NET подробно, со всеми его механизмами
• Создавать различные технологии межсервисного взаимодействия + реалтайм с клиентским приложением
•Интегрировать фронтенд (ReactJS + JavaScript+Typescript) с бэкендом
• Тестировать приложения: интеграционные и нагрузочные тесты
• Автоматизировать процессы с CI/CD и Kubernetes
• Проектировать микросервисы и освоить event-driven архитектуру

🎁 Бонус: После теста — доступ к урокам!

👉 Проверьте свои силы прямо сейчас:  https://otus.pw/gmbX/?erid=2W5zFJd7K3v

Реклама. ООО "ОТУС ОНЛАЙН-ОБРАЗОВАНИЕ". ИНН 9705100963.
😈 Хитрая задачка на 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# захватывают переменные, а не их значения! Аккуратнее с циклами и лямбдами.