C# Portal | Программирование – Telegram
C# Portal | Программирование
14.9K subscribers
965 photos
117 videos
24 files
807 links
Присоединяйтесь к нашему каналу и погрузитесь в мир для C#-разработчика

Связь: @devmangx

РКН: https://clck.ru/3FocB6
Download Telegram
Разбираем генераторы исходного кода.

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

Генераторы исходного кода можно использовать для разных задач: скафолдинга, валидации, повышения читаемости и упрощения поддержки кода.

В C# 12 генераторы получили дальнейшее развитие. Появилась поддержка более сложных сценариев и выросла производительность за счёт уменьшения шаблонного кода и более эффективных проверок на этапе компиляции.

1. Инкрементные генераторы

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

2. Анализ зависимостей исходного кода

Компилятор точнее определяет, какие части проекта зависят от сгенерированного кода. В результате сборки становятся эффективнее, а количество лишних перекомпиляций сокращается.

3. Улучшенная диагностика

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

4. Улучшения API Roslyn

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

Простой пример генератора кода

1. Настройка проекта

Создадим проект библиотеки и установим необходимые NuGet-пакеты:

dotnet new classlib -n MySourceGenerator
cd MySourceGenerator
dotnet add package Microsoft.CodeAnalysis.CSharp


2. Генератор кода

Нам понадобится класс, реализующий интерфейс IIncrementalGenerator:

using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;

[Generator]
public class HelloWorldGenerator : IIncrementalGenerator
{
public void Initialize(
IncrementalGeneratorInitializationContext context)
{
var src =
"""
using System;
namespace HelloGenerated;
public static class HelloWorld
{
public static void SayHello()
=> Console.WriteLine("Hello from the generated code!");
}
""";

context.RegisterPostInitializationOutput(
ctx => ctx.AddSource(
"HelloWorldGenerated",
SourceText.From(src, Encoding.UTF8)));
}
}


Этот генератор добавляет в проект класс HelloWorld с методом SayHello. Сгенерированный код компилируется вместе с основным проектом, поэтому метод HelloGenerated.HelloWorld.SayHello() можно вызывать напрямую из вашего кода.

3. Интеграция с проектом

Создадим консольное приложение:

dotnet new console -n UseSourceGenerator


Добавим ссылку на проект генератора:

<ProjectReference
Include="..\MySourceGenerator\MySourceGenerator.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false" />


Обратите внимание на два нестандартных атрибута:

OutputItemType="Analyzer" — указывает, что проект должен использоваться как анализатор;

ReferenceOutputAssembly="false" — гарантирует, что целевой проект не будет ссылаться на DLL генератора во время компиляции.

После этого сгенерированный класс HelloWorld станет доступен в консольном проекте.

4. Сборка и запуск

Соберём проект и запустим его. В консоли появится вывод из сгенерированного метода:

// Program.cs
HelloGenerated.HelloWorld.SayHello();


В след. посте про чтение кода из файла 😌

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
8👍1🤔1
This media is not supported in your browser
VIEW IN TELEGRAM
Это рай для программистов: репозиторий с сотнями бесплатных API.

Отлично подходит для практики и прокачки навыков программирования

Забираем здесь 🥶

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8
Знаешь, что в .NET 10 сильно снизили накладные расходы при работе с любыми встроенными коллекциями через интерфейсы (да, не только с массивами)?

Итерация по списку через IList<T> или по словарю через IDictionary<K, V> и т.п. теперь почти не даёт абстрактного оверхеда.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11🤯11🔥42
Держи инструмент для запросов к данным, если уже надоели классические клиенты для БД: agx

Внутри есть умный SQL-редактор с подсветкой синтаксиса и помощью LLM при написании запросов, браузер схем для быстрого просмотра структуры данных, а также импорт данных из файлов через drag-and-drop.

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

🛌

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
Разбираем Server-Sent Events в ASP.NET Core и .NET 10.

Обновления UI в реальном времени больше не считаются просто желательной фичей. Большинство современных приложений ожидают поток данных в реальном времени от сервера. Долгие годы основным решением в экосистеме .NET был SignalR. Он действительно очень мощный, но для более простых сценариев приятно иметь альтернативы.

В ASP.NET Core 10 появился собственный высокоуровневый API для Server-Sent Events (SSE). Он закрывает разрыв между примитивным HTTP-поллингом и полнодуплексными WebSockets через SignalR.

Зачем?

SignalR — инструмент, который автоматически работает с WebSockets, Long Polling и SSE, предоставляя полнодуплексный (двусторонний) канал связи. Но за это приходится платить: свой протокол, обязательная клиентская библиотека и необходимость в «липких сессиях» или отдельном бэкенде (например, Redis) для масштабирования.

В отличие от SignalR, SSE:

- Однонаправленные - разработаны специально для потоковой передачи данных с сервера на клиент.
- Нативны для HTTP - это стандартный HTTP-запрос с типом содержимого text/event-stream. Никаких пользовательских протоколов.
- Поддерживают автоматическое переподключение - браузеры обрабатывают переподключения напрямую через API EventSource.
- Легковесны - нет тяжёлых клиентских библиотек или сложной логики рукопожатия.

Простейшая SSE-конечная точка

Мы можем использовать новый объект Results.ServerSentEvents, чтобы вернуть поток событий из любого IAsyncEnumerable<T>. Поскольку IAsyncEnumerable представляет поток данных, который может приходить со временем, сервер понимает, что HTTP-соединение нужно держать открытым, а не закрывать его после первого куска данных.

Минимальный пример SSE-эндпоинта, который в реальном времени передаёт события о размещении заказов:

app.MapGet("orders/realtime", (
ChannelReader<OrderPlacement> reader,
CancellationToken ct) =>
{
// ReadAllAsync возвращает IAsyncEnumerable
// Results.ServerSentEvents заставляет браузер держать соединение открытым
// Новые данные отправляются клиенту по мере поступления из канала
return Results.ServerSentEvents(
reader.ReadAllAsync(ct),
eventType: "orders");
});


Когда клиент обращается к этой конечной точке:

Сервер отправляет заголовок Content-Type: text/event-stream.

Соединение остаётся открытым и ждёт данные.

Как только приложение пишет новый заказ в канал, IAsyncEnumerable возвращает элемент, и .NET сразу отправляет его в браузер по открытому HTTP-соединению.

Это очень эффективный способ реализовать push-уведомления без накладных расходов, характерных для stateful-протоколов.

В примере используется Channel. В реальном приложении это может быть фоновый сервис, который слушает очередь сообщений (например, RabbitMQ или Azure Service Bus) или поток изменений БД и прокидывает новые события в канал для доставки подключённым клиентам.

В след. посте поговорим про обработку пропущенных событий 👍

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥15👍6👏21
📱 Держите 6 хороших каналов по искусственному интеллекту и программированию для любого уровня!

Выбирай направление:

📱 Нейросети@neuro_prompt

🤖 AI-инструменты @ai_prompt

📱 Python@python_prompt

🤔 InfoSec & Хакинг @infosec_prompt

👩‍💻 IT Новости @it_news

😄 IT Мемы@it_memes

Промпты, обучение, шпаргалки и полезные ресурсы на каждую тему!
Please open Telegram to view this post
VIEW IN TELEGRAM
2👎1🥴1
EF Core наконец поправил проблему, вокруг которой мы делали костыли годами.

Фикс завезли в EF 10

В EF Core 10 глобальные query filters переделали в named query filters.

Global query filters в Entity Framework Core — это мощная фича для управления паттернами доступа к данным.

Глобальные query filters — это предикаты LINQ, применяемые к entity-моделям EF Core.

Фильтры автоматически накладываются на все запросы, где участвуют соответствующие сущности. Особенно полезно для multi-tenant приложений или soft delete сценариев.

Апдейт в EF 10 закрыл одно серьёзное ограничение EF Core: теперь можно вешать несколько global query filters на одну сущность.

В реальном приложении нам часто нужно иметь несколько фильтров на одну и ту же сущность, например:
• один для soft delete
• второй для multi-tenancy

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍63
Топ-20 NuGet пакетов для .NET разработчиков в 2026

Не надо изобретать колесо

Вот список отличных библиотек, которые я лично использовал и которые могу смело порекомендовать для эффективной backend-разработки на .NET:

1. Entity Framework Core

2. Dapper

3. Serilog

4. FluentValidation

5. Polly

6. Refit

7. Wolverine

8. Dapr

9. Quartz .NET

10. Swagger (Swashbuckle)

11. xUnit

12. Shouldly

13. TestContainers

14. Bogus

15. Moq

16. FluentEmail

17. FastEndpoints

18. HotChocolate GraphQL

19. SignalR

20. UnitsNet


👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍107😐2
Обработка пропущенных событий

У простой конечной точки, которую мы делали ранее , есть один минус: у неё нет отказоустойчивости.

Одна из самых неприятных проблем с потоками в реальном времени — обрывы соединения. Пока браузер автоматически переподключится, сервер уже может отправить несколько событий, и они потеряются. Для этого в SSE предусмотрен встроенный механизм — заголовок Last-Event-ID. Когда браузер переподключается, он отправляет этот ID обратно на сервер.

В .NET 10 можно использовать тип SseItem<T> для добавления метаданных к данным (ID и retry-интервалы).

Можно завести простой OrderEventBuffer в памяти, который содержит объекты SseItem<OrderPlacement> и умеет отдавать все элементы после Last-Event-ID, который пришлёт браузер. Так мы можем «переиграть» пропущенные сообщения при переподключении:

app.MapGet("orders/realtime/with-replays", (
ChannelReader<OrderPlacement> reader,
OrderEventBuffer buffer,
[FromHeader(Name = "Last-Event-ID")]
string? lastEventId,
CancellationToken ct) =>
{
async IAsyncEnumerable<SseItem<OrderPlacement>>
StreamEvents()
{
// «Доливаем» пропущенные события
if (!string.IsNullOrWhiteSpace(lastEventId))
{
var missed = buffer.GetEventsAfter(lastEventId);
foreach (var m in missed)
yield return m;
}

// Отдаём новые события по мере появления в канале
await foreach (var order in reader.ReadAllAsync(ct))
{
// Буфер назначает уникальный ID
var sseItem = buffer.Add(order);
yield return sseItem;
}
}

return TypedResults.ServerSentEvents(
StreamEvents(), "orders");
});


Далее фильтрация SSE по пользователю - ❤️

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8
Худший вид бага тот, который взрывается через 3 дня после деплоя.

Это скрытая проблема стандартного Options Pattern в .NET.

Ты биндишь appsettings.json к классу, инжектишь его, всё запускается, ошибок нет. Жизнь вроде хороша.

Но если, например, отсутствует обязательный API Key? Узнаешь об этом только когда пользователь дёрнет нужный сценарий, и приложение упадёт.

И вот тут важно правило — конфиг должен валиться сразу. Fail Fast.

Если конфигурация некорректна, приложение не должно даже стартовать. Точка.

Исправляется это расширением Options Pattern через IValidateOptions.

Вместо простого биндинга ты навешиваешь валидацию:

Определяешь правила: ApiKey не null, RetryCount > 0 и т.п.

1. Регистрируешь валидатор
2. Если конфиг не проходит правила, DI выбрасывает исключение на старте
3. Автор пошёл дальше и прикрутил FluentValidation, чтобы правила выглядели чище и понятнее.

Полная реализация тут

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11👍42🤯2🤔1
Toolbox что это вообще такое?

Для начала, сам Toolbox лежит тут: [https://toolbox.bitspire.ch/]

Toolbox это набор утилит, которые облегчают жизнь разработчика. Часть функций ориентирована на новичков в языке с упором на веб-разработку. На текущий момент в наборе есть:

• JSON → C# (да, это уже тысячу раз видели, но инструмент обязателен, и тут он ещё и настраиваемый)
• JSON → TypeScript (и обратно)
• JWT decoder
• Middleware Designer — генератор ASP.NET middleware (особенно полезен новичкам)
• Package centralizer — генерирует Directory.Packages.props на основе csproj-файлов
• C# Mindmap — обзор всех фич в одном месте

Хочу отдельно выделить две фичи, которые я пока нигде больше не встречал.

1. Middleware Designer

Middleware Designer это простой старт для новичков, который помогает понять какие middleware обычно используются в .NET и как их конфигурировать. Инструмент также пытается подсвечивать потенциальные проблемы. В примере на фото 1 видно, что Authorization вызывается раньше Authentication, что вряд ли то, что нужно.

Есть ещё режим "симуляции" запросов, чтобы посмотреть вживую как работают всякие штуки вроде rate-limiting. В идеале это даёт более понятную картинку типичных пайплайнов в ASP.NET и то, как их настраивать и использовать.

2. Package Centralizer

Ещё одна штука, которую я не встречал в открытом виде — генерация Directory.Packages.props из набора csproj-файлов. По сути вы подаёте пачку csproj-ов и получаете централизованную конфигурацию зависимостей (с опциональным разрешением конфликтов) + обновлённые csproj-файлы.

Если интересно покопаться:

GitHub tracker для фичреквестов и багов

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
3🥴2
Числовая сортировка строк в .NET

После десятилетия споров .NET наконец получил нормальную сортировку строк с числами. Кейс file1, file10, file2 теперь сортируется так, как ожидают люди, а не как ожидает лексикографический порядок.

Фишка в новом флаге CompareOptions.NumericOrdering. Вместо посимвольного сравнения цифры трактуются как числа. Это решает старую проблему с файлами, версиями и прочими строками, где встречаются числа.

Идея обсуждалась ещё в 2015 году в issue#13979, зависла на годы и только недавно доехала нативно в стек. Сейчас это уже в .NET 10.

Протестить:
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;

var files = new List<string> { "file10", "file2", "file1", "file20" };

// Обычная сортировка
var sortedLex = files.OrderBy(f => f).ToList();
Console.WriteLine("Лексикографическая: " + string.Join(", ", sortedLex));
// file1, file10, file20, file2

// Числовая сортировка
var comparer = StringComparer.Create(CultureInfo.CurrentCulture, CompareOptions.NumericOrdering);
var sortedNumeric = files.OrderBy(f => f, comparer).ToList();
Console.WriteLine("Числовая: " + string.Join(", ", sortedNumeric));
// file1, file2, file10, file20


Работает через:

StringComparer.Create(CultureInfo.CurrentCulture, CompareOptions.NumericOrdering)


👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
11🔥8👍2
Форматировщик C# в VSCode

1. Установите расширение C#

2. Перезапустите VS Code, чтобы расширение применилось.

3. Создайте .editorconfig в текущей директории по умолчанию или в родительской.
Я создал его в C:\Users\user, чтобы можно было использовать отовсюду.

Пример .editorconfig:

root = true

[*.cs]
indent_size = 2
indent_style = space
csharp_new_line_before_open_brace = none # не переносить фигурные скобки


Добавьте в settings.json:

"[csharp]": {
"editor.defaultFormatter": "ms-dotnettools.csharp",
}


Теперь форматтер для C# должен быть настроен.

Предупреждение:
На версии 2.100.5 и выше .editorconfig не применялся.
На 2.97.38 всё работало, так что пришлось откатиться.
Откройте расширение, нажмите стрелку рядом с Uninstall, там будет выбор конкретной версии.

Список проверки:

2.93.22    работает
2.94.41 работает
2.96.3 работает
2.97.38 работает
2.100.5 не работает
2.110.4 не работает


До версии 2.96 при нажатии Enter форматирование срабатывает автоматически,
потому что расширение включает editor.formatOnType.
Поэтому нужно переопределить его в settings.json:

"[csharp]": {
"editor.defaultFormatter": "ms-dotnettools.csharp",
"editor.formatOnType": false
}


PR, который исправляет editor.formatOnType

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
3
Вы кидаете исключения, когда валидация падает?

Я пару раз видел такие реализации.

Глобальный обработчик исключений превращает исключение в ответ API.

Я согласен с этим, если это правда исключительная ситуация (что на самом деле бывает не так уж часто).

Куда лучше возвращать какой-нибудь результат с ошибкой, чтобы не платить за оверхед выбрасывания исключений.

Вот решение, которое можно попробовать

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👎8
Зафиксируй самые важные файлы в самом верху Solution Explorer, не важно находятся они в солюшене или просто где-то на диске. Больше никакого копания по папкам.

Ставь расширение для Visual Studio

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥10👍2
В C# 14 появилась возможность перегружать составные операторы присваивания вроде += и -=.

Перегрузка оператора +

Когда нужно складывать объекты между собой, раньше приходилось перегружать оператор +.

Допустим есть класс Counter и у него есть поле (или свойство) Value типа int:

public class Counter
{
public Counter(int value)
{
Value = value;
}

public int Value { get; set; }
}


Вот так сложить два Counter через оператор + нельзя:

var c1 = new Counter(1);
var c2 = new Counter(10);
var c3 = c1 + c2;


Компилятор не понимает что значит сложить два объекта, поэтому вылетает ошибка. Но если заранее определить что именно должен делать оператор +, код станет рабочим:

public class Counter
{
public Counter(int value)
{
Value = value;
}

public int Value { get; set; }

public static Counter operator +(Counter a, Counter b)
{
return new Counter(a.Value + b.Value);
}
}


При перегрузке оператора + объявляется статический метод, который принимает два объекта одного типа, реализует нужную логику и возвращает новую инстанцию. В примере выше просто суммируются Value и создается новый Counter.

После этого пример с суммой компилируется и работает как ожидается:

var c1 = new Counter(1);
var c2 = new Counter(10);

var c3 = c1 + c2; // так можно было и до C# 13


Перегрузка оператора +=


Начиная с C# 14, можно перегружать составные операторы присваивания вроде += или -=. Запись выглядит так:

public class Counter
{
public Counter(int value)
{
Value = value;
}

public int Value { get; set; }

public static Counter operator +(Counter a, Counter b)
{
return new Counter(a.Value + b.Value);
}

public void operator +=(Counter other)
{
this.Value += other.Value;
}
}


Перегрузка оператора += в C# 14 — это инстанс-метод, который принимает объект, с которым нужно произвести операцию. В отличие от статической перегрузки +, здесь не нужно возвращать новый объект: можно модифицировать текущий через this.

Использование выглядит так:

var c1 = new Counter(1);
var c2 = new Counter(10);

c1 += c2;


После выполнения Value из c2 будет добавлен к Value у c1. Новая инстанция не создается, что даёт лучшую производительность.

Кроме +=, можно перегружать и другие составные операторы: -=, *=, /=, %=, &=, |=, ^=, <<=, >>= и так далее.


👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15🔥7
В C# 14 появилась возможность ставить вопросительный знак у переменной при присвоении и тем самым выполнять null-проверку перед записью значения.

Теперь можно писать так:

Product? p = null;
p?.ProductId = 999; // в C# 14 так уже можно


Это эквивалентно следующему:

// компилятор разворачивает во что-то подобное
if (p != null)
{
p.ProductId = 999;
}


До C# 14 вопросительный знак тоже можно было использовать для null-проверок, например:

Product? p = null;

if (p?.ProductId == 1) // так можно было
{
}


Но если написать так, до C# 13 это вызывало ошибку:

p?.ProductId = 999; // до C# 13 так нельзя


До C# 14 доступ к полям/свойствам через ?. был разрешен только на чтение. То есть конструкция трактовалась как использование значения: можно было проверить p?.ProductId == ..., узнать null это или число и так далее. Но запись выглядела неоднозначно, потому что получалось будто компилятор должен понять: "присваиваем null?" или "куда именно пишем?". Он не мог корректно интерпретировать это как условную запись.

В обновлении C# 14 поведение уточнили: ? рассматривается как условие вида "если объект не null". Благодаря этому присвоение стало корректным и теперь разрешено.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥12👍8👨‍💻2