C# (C Sharp) programming
18.1K subscribers
969 photos
52 videos
8 files
774 links
По всем вопросам- @notxxx1

Реестр РКН: https://clck.ru/3Fk3kb

#VRHSZ
Download Telegram
## В C# появился safe — но это не аналог Rust

В C# 15 тестируется новый контекстный модификатор safe для кода на границе с нативной памятью.

Главная проблема P/Invoke: компилятор видит сигнатуру метода, но не может проверить, насколько безопасно ведёт себя код внутри подключённой библиотеки. Теперь разработчик должен явно зафиксировать решение:


[LibraryImport("libc")]
internal static safe partial int getpid();

[LibraryImport("libc")]
internal static unsafe partial nuint strlen(byte* value);


safe означает, что вызов не требует `unsafe`-контекста со стороны пользователя API.

unsafe предупреждает: безопасность зависит от условий, которые компилятор проверить не может. Такой метод разрешено вызывать только внутри блока:


unsafe
{
nuint length = strlen(pointer);
}


Та же логика применяется к полям структур с явным расположением памяти:


[StructLayout(LayoutKind.Explicit)]
struct Packet
{
[FieldOffset(0)]
public safe long Id;

[FieldOffset(0)]
public unsafe nint Pointer;
}


Если не указать ни safe, ни unsafe, компилятор выдаст ошибку при включённых новых правилах безопасности. :contentReference[oaicite:0]{index=0}

Важный нюанс: safe не анализирует нативный код и не доказывает его безопасность. Это явное обещание автора API, которое делает потенциально опасные границы заметными при ревью.

C# постепенно меняет подход к unsafe: риск должен быть обозначен в контракте метода и локализован в конкретных участках программы, а не спрятан внутри большого `unsafe`-класса.

Пока эта модель находится в preview и может измениться до стабильного выпуска C# 15.
🔥 Request-response через брокер сообщений в C#: мощный паттерн, который легко превратить в распределённый дедлок

Один сервис отправляет сообщение в RabbitMQ или NATS и ждёт ответ.

Для вызывающего кода это выглядит почти как обычный await, хотя под капотом работают две независимые очереди:


Requester -> request -> Responder
Requester <- response <- Responder


В RabbitMQ запрос обычно содержит ReplyTo и уникальный CorrelationId, чтобы клиент понял, к какому запросу относится ответ.

В NATS для ответа создаётся отдельный inbox subject.

Упрощённая идея на C#:


var requestId = Guid.NewGuid();

await bus.SendAsync(new PriceRequest(
RequestId: requestId,
ProductId: productId
));

var response = await responses
.WaitForAsync<PriceResponse>(
requestId,
timeout: TimeSpan.FromSeconds(2),
cancellationToken);


Плюсы действительно значительные:

- отправителю не нужно знать адрес обработчика;
- responder можно горизонтально масштабировать;
- брокер помогает пережить кратковременный сбой сервиса;
- приложения остаются слабо связанными.

Но это не «HTTP, только через RabbitMQ».

Пока requester ждёт результат, взаимодействие остаётся логически синхронным. Поэтому обязательно нужны:

- таймаут и CancellationToken;
- уникальный CorrelationId;
- обработка поздних и повторных ответов;
- идемпотентность responder;
- ограничение количества одновременных запросов;
- трассировка через обе очереди.

Особенно опасный случай:


Service A ждёт B
Service B ждёт C
Service C ждёт A


Брокер работает, сообщения доставляются, но вся система стоит.

Request-response через messaging полезен, когда нужны location transparency, балансировка обработчиков и единый транспорт между сервисами.

Для простого короткого запроса HTTP или gRPC часто остаются понятнее и быстрее.

Главное правило:

брокер убирает прямую связь между сервисами, но не убирает зависимость от ответа.
Please open Telegram to view this post
VIEW IN TELEGRAM
💡 C#: заставьте архитектуру ломать CI, если кто-то нарушил правила

Компилятор C# отлично проверяет типы, но ему всё равно, что Application внезапно начал зависеть от Infrastructure.

Code Review тоже не гарантирует, что такое заметят.

Для этого можно использовать Architecture Tests — тесты, которые проверяют не бизнес-логику, а структуру проекта.

Например, через NetArchTest.Rules:


[Fact]
public void Application_Should_Not_Depend_On_Infrastructure()
{
var result = Types
.InAssembly(ApplicationAssembly)
.ShouldNot()
.HaveDependencyOn("MyApp.Infrastructure")
.GetResult();

Assert.True(result.IsSuccessful);
}


Теперь случайный:


Application → Infrastructure


сломает CI так же, как обычный упавший unit-тест.

Можно зафиксировать и naming convention:


[Fact]
public void Handlers_Should_End_With_Handler()
{
var result = Types
.InAssembly(ApplicationAssembly)
.That()
.ImplementInterface(typeof(ICommandHandler<>))
.Should()
.HaveNameEndingWith("Handler")
.GetResult();

Assert.True(result.IsSuccessful);
}


Или заставить сервисы быть sealed:


Types
.InAssembly(ApplicationAssembly)
.That()
.HaveNameEndingWith("Service")
.Should()
.BeSealed();


Особенно полезно это становится в больших Clean Architecture / DDD / Modular Monolith проектах.

Вместо документа:


«Application не должен зависеть от Infrastructure»


получаем исполняемое правило:


нарушил архитектуру → тест упал → PR не прошёл


По сути, архитектура превращается из договорённости команды в часть автоматической проверки проекта.

#CSharp #DotNet #Architecture #Testing
🚀 C# РАЗРАБОТЧИКИ: перестаньте создавать зависимости вручную

Одна из самых частых ошибок в архитектуре .NET - создавать зависимости прямо внутри класса.

Плохой подход:
var companyRepository = new CompanyRepository();
var customerRepository = new CustomerRepository();
var creditService = new CustomerCreditServiceClient();
Проблемы:

жёсткая связность компонентов
сложно заменить реализацию
трудно писать unit-тесты
нет удобного контроля времени жизни объектов

Лучше использовать Dependency Injection
public class CustomerService(
CompanyRepository companyRepository,
CustomerRepository customerRepository,
CustomerCreditServiceClient creditService)
{
}
Теперь DI-контейнер сам создаёт и передаёт нужные зависимости.

Преимущества:

управление временем жизни (Singleton, Scoped, Transient)
лёгкое использование mock-объектов в тестах
decorators для логирования, кэширования и retry
меньше связности между классами
проще масштабировать и рефакторить проект

Dependency Injection - это не просто возможность ASP.NET Core.

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

🔥 Ещё 5 приёмов рефакторинга C#, которые стоит знать каждому .NET разработчику:
https://milanjovanovic.tech/blog/5-awesome-csharp-refactoring-tips
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥 .NET 11 Preview 7 вышел, и Microsoft заметно прокачала сразу C#, Runtime, ASP.NET Core и SDK

Один из самых интересных апдейтов в C# — labeled break и continue: теперь можно явно указать, из какого цикла выходить или какой цикл продолжать. Ещё появились union patterns и улучшенная проверка исчерпывающих случаев для закрытых иерархий типов.

В Runtime добавили оптимизации async`/`await, включая runtime async tiering и tail-await. Отдельно продолжают развивать NativeAOT и JIT.

В SDK NativeAOT-версия dotnet CLI теперь включена по умолчанию, как и MSBuild Server. У dotnet test появились общий --timeout, ограничение --maximum-failed-tests и поддержка запуска тестов на устройствах .NET MAUI.

ASP.NET Core получил автоматическую приостановку неактивных Blazor circuits, кеширование SSR через CacheView, новые анализаторы Blazor и встроенную локализацию validation-сообщений.

Ещё из приятного: HTTP request compression, API для DNS-записей, парольная защита ZIP, Complex<T>, улучшения LINQ в EF Core и поддержка Half в SQLite.

.NET 11 Preview 7 вышел 11 августа 2026 года и уже доступен для тестирования.

https://devblogs.microsoft.com/dotnet/dotnet-11-preview-7/
⚙️ Service Discovery в .NET через Consul можно подключить без самописного резолва адресов и ручного перебора инстансов.

Рабочая схема выглядит так:


Install-Package Steeltoe.Discovery.Consul


Регистрируем discovery:


builder.Services
.AddServiceDiscovery(o => o.UseConsul());


В appsettings.json указываем Consul и параметры сервиса:


"Consul": {
"Host": "localhost",
"Port": 8500,
"Discovery": {
"ServiceName": "reporting-service",
"Hostname": "reporting-api",
"Port": 8080
}
}


А дальше самое полезное:


builder.Services
.AddHttpClient<ReportingServiceClient>(client =>
{
client.BaseAddress = new Uri("http://reporting-service");
})
.AddServiceDiscovery()
.AddRoundRobinLoadBalancer();


То есть HttpClient ходит не на конкретный IP:port, а на логическое имя сервиса.

Steeltoe через Consul получает живые инстансы, а RoundRobinLoadBalancer распределяет запросы между ними.

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

По сути, клиентская часть получает простой pipeline:

service name → Consul → healthy instances → load balancing → HTTP request

Для микросервисов на .NET это намного чище, чем городить собственный discovery layer.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥 Хочешь быстрее расти в IT? Хватит учиться в одиночку

Окружение решает больше, чем кажется.

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

AI: t.me/ai_machinelearning_big_data
Python: t.me/pythonl
Linux: t.me/linuxacademiya
Хакинг: t.me/linuxkalii
DevOps: t.me/DevOPSitsec
Docker: https://xn--r1a.website/+90Z5TAyfuNU5YmRi
Golang: t.me/Golang_google
Rust: t.me/rust_code
C++: t.me/cpluspluc
C#: t.me/csharp_1001_notes
Java: t.me/javatg
JavaScript: t.me/javascriptv
React: t.me/react_tg
Frontend: t.me/front
PHP: t.me/phpshka
Android: t.me/android_its
Мобильная разработка: t.me/mobdevelop
Базы данных: t.me/sqlhub
Data Science: t.me/data_analysis_ml
Big Data: t.me/bigdatai
Математика: t.me/data_math
Физика: https://xn--r1a.website/+S4hinvO3QI43ZjNi
Kubernetes: t.me/kubernetc
GameDev: https://xn--r1a.website/gamedev
Haskell: t.me/haskell_tg

Собеседования и карьера:

DS собеседования: t.me/machinelearning_interview
Python собеседования: t.me/python_job_interview

Папка с вакансиями: t.me/addlist/_zyy_jQ_QUsyM2Vi
Папка Go разработчика: t.me/addlist/MUtJEeJSxeY2YTFi
Папка Python разработчика: t.me/addlist/eEPya-HF6mkxMGIy
Папка ML: https://xn--r1a.website/addlist/2Ls-snqEeytkMDgy
Папка Frontend: https://xn--r1a.website/addlist/mzMMG3RPZhY2M2Iy

Полезное сверху:

ИТ-мемы: t.me/memes_prog
Английский для программистов: t.me/english_forprogrammers
ИИ и технологии: t.me/vistehno
954 ГБ open-source курсов: https://xn--r1a.website/+rKBQEMccAA01MTcy
ИТ-книги бесплатно: https://xn--r1a.website/addlist/BkskQciUW_FhNjEy

Max Ai: https://max.ru/ai_machinelearning_big_data
Max python: https://max.ru/pythonl
ТЕХНО: https://max.ru/vistehno
Max Go: https://max.ru/Golang_google
Max Linux: https://max.ru/linuxkalii
Devops: https://max.ru/DevOPSitsec
C#: https://max.ru/csharp_ci
C++: https://max.ru/cpluspluc
SQL: https://max.ru/sqlhub
Java: https://max.ru/javatg

Подписывайся на нужные направления и собирай себе ленту, которая реально двигает вперёд.
Появился бесплатный open-source PDF-редактор для Windows, который пытается заменить Adobe Acrobat без подписки.

KillerPDF умеет редактировать текст прямо в PDF, добавлять аннотации и изображения, объединять и разделять страницы, заполнять формы, ставить подписи, печатать и делать OCR. Причём распознавание работает локально через Tesseract, без отправки документов в облако.

Есть и более редкие штуки: восстановление повреждённых PDF, открытие файлов с паролем, выравнивание кривых сканов, perspective correction, ночной режим, split view для двух документов и полноценная работа из командной строки.

Весь редактор упакован примерно в 16 МБ, может работать portable, не требует аккаунта, не собирает телеметрию и не делает phone-home. Проект распространяется под GPLv3.

На GitHub уже около 3,4 тыс. звёзд.

https://github.com/SteveTheKiller/KillerPDF
Как ограничить количество запросов к API по IP в ASP.NET Core

Встроенный Rate Limiter позволяет сделать это буквально в несколько строк:


builder.Services.AddRateLimiter(options =>
{
options.AddPolicy("fixed-by-ip", httpContext =>
RateLimitPartition.GetFixedWindowLimiter(
partitionKey: httpContext.Connection.RemoteIpAddress?.ToString(),
factory: _ => new FixedWindowRateLimiterOptions
{
PermitLimit = 10,
Window = TimeSpan.FromMinutes(1)
}));
});


В этом примере один IP может сделать максимум 10 запросов за минуту.

Но использовать такой подход вслепую не стоит.

Причина простая: реальные пользователи могут находиться за NAT, прокси или корпоративным шлюзом и иметь один внешний IP. В итоге лимит одного человека начнёт влиять на остальных.

Поэтому для авторизованных пользователей обычно лучше ограничивать запросы по уникальному userId.

А rate limit по IP оставлять для анонимных endpoint'ов, защиты от простых ботов или как дополнительный слой защиты.
🚀 .NET 8: как сделать микросервисы устойчивыми с Resilience Pipelines + DI

В распределённых системах ошибки — это не исключение, а реальность:

API временно недоступен
сеть дала сбой
сервис перегружен
внешний провайдер отвечает слишком долго

В .NET 8 это стало намного проще благодаря Microsoft.Extensions.Resilience (на базе Polly).

Можно собрать свой ResiliencePipeline и добавить в DI один раз:

Retry — повторить запрос
Timeout — ограничить время ожидания
Circuit Breaker — остановить поток ошибок
Fallback — вернуть запасной результат
Rate Limiter — защитить сервис
Hedging — отправить альтернативный запрос

Главный плюс — не нужно настраивать защиту в каждом месте кода.

Создаёшь pipeline → регистрируешь через DI → используешь где нужно:


var user = await pipeline.ExecuteAsync(
async token => await httpClient.GetAsync("api/users", token)
);


Чистый код, единая стратегия обработки ошибок и меньше боли при росте микросервисов.

Если пишете на C# и строите production-системы — Resilience Pipelines стоит добавить в свой toolbox. ⚙️
Please open Telegram to view this post
VIEW IN TELEGRAM
Microsoft выпустила августовские servicing-обновления для .NET и .NET Framework

Обновления вышли 11 августа 2026 года и включают как security, так и non-security fixes.

Актуальные версии:

- .NET 10.0.11
- .NET 9.0.19
- .NET 8.0.30

В этом месяце Microsoft закрыла сразу несколько уязвимостей в .NET 8, 9 и 10, включая:

- Remote Code Execution
- Elevation of Privilege
- Information Disclosure
- Denial of Service
- Security Feature Bypass

Среди исправлений есть CVE-2026-70354 и CVE-2026-62897, связанные с удалённым выполнением кода.

Обновлены также:

- ASP.NET Core
- Entity Framework Core
- Runtime
- container images
- Linux packages

Для .NET Framework тоже вышли новые security и non-security updates.

Если приложение работает на .NET 8, 9 или 10, августовский servicing update лучше не откладывать, особенно из-за набора security fixes.

Подробнее:
https://devblogs.microsoft.com/dotnet/dotnet-and-dotnet-framework-august-2026-servicing-updates/

#aspnetcore #dotnet
Как упростить работу с иерархическими данными в Blazor

Обычный DropDownList удобен для плоских списков, а TreeView хорошо показывает структуру, но занимает много места.

Telerik предлагает промежуточный вариант - DropDownTree.

Он позволяет выбирать элементы из дерева прямо внутри компактного dropdown:

* категории товаров
* структура компании
* файловая система
* регионы
* роли и permissions

Компонент поддерживает вложенные данные напрямую, поиск по дереву, раскрытие нужных узлов и отображение выбранного пути.

Пример:


<TelerikDropDownTree Data="@Data"
@bind-Value="@SelectedValue"
ValueField="TreeItemId"
Filterable="true">

<DropDownTreeBindings>
<DropDownTreeBinding TextField="Text"
ItemsField="Items" />
</DropDownTreeBindings>

</TelerikDropDownTree>


Главное преимущество - не нужно превращать сложную иерархию в огромный плоский список.

Если данные уже представлены как рекурсивный DTO, их часто можно передать компоненту практически напрямую.

Полезный вариант для Blazor-приложений, где пользователь должен выбирать что-то из большого дерева данных, но постоянно держать TreeView на экране нет смысла.

https://telerik.com/blogs/simplify-hierarchical-selection-blazor-telerik-dropdowntree

#aspnetcore #blazor
⚡️ Как нормально работать с конфигурацией в .NET

Значения могут приходить из appsettings.json, environment variables, User Secrets и других configuration providers.

Можно читать их напрямую через IConfiguration, но такой код быстро становится хрупким и неудобным.

Вместо этого лучше использовать Options Pattern:

* создать класс настроек
* привязать его к секции конфигурации
* получать через IOptions<T>
* добавить валидацию через Data Annotations

Так конфигурация становится типизированной, проще тестируется и меньше ломается при рефакторинге.
🚀 Microsoft показала, как переносить старые приложения с .NET Framework на .NET 10 прямо в Visual Studio с помощью GitHub Copilot.

Copilot теперь помогает пройти модернизацию по шагам:

- анализирует старый проект
- составляет план обновления
- показывает контрольные точки перед изменениями
- помогает переписывать несовместимые части
- сохраняет ход миграции в Markdown-файлах
- делает процесс прозрачнее и проще для проверки командой

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

Полезно для команд, которые годами откладывали переход со старого .NET Framework из-за объёма ручной работы.

https://devblogs.microsoft.com/visualstudio/today-i-will-modernize-a-net-application/
🧩 Как собрать Excel-подобную таблицу в .NET MAUI через Blazor Hybrid

В статье показывают, как встроить полноценный spreadsheet-интерфейс в .NET MAUI-приложение с помощью Blazor Hybrid.

Что получается:

- таблица в стиле Excel
- редактирование ячеек
- формулы и вычисления
- форматирование
- работа с листами
- импорт и экспорт Excel-файлов
- запуск в desktop/mobile-приложении на .NET MAUI

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

https://syncfusion.com/blogs/post/blazor-spreadsheet-dotnet-maui
Please open Telegram to view this post
VIEW IN TELEGRAM