23284
Все самое полезное для C#-разработчика в одном канале. По рекламе: @proglib_adv Учиться у нас: https://proglib.io/w/b60af5a4 Для обратной связи: @proglibrary_feeedback_bot РКН: https://gosuslugi.ru/snet/67a5c81cdc130259d5b7fead
💻 Плавающие окна в Visual Studio
Если вы работаете с несколькими мониторами, вы наверняка отрывали Solution Explorer или окно отладчика на второй экран. Это удобно, но поведение плавающих окон по умолчанию бывает раздражающим: они не отображаются отдельными кнопками в панели задач Windows, сворачиваются вместе с основным окном IDE и всегда остаются поверх остальных окон.
В Visual Studio есть настройка, которая это меняет.
Где найтиTools > Options > Environment > Windows > Floating Windows
Там есть выпадающий список «These floating windows are owned by the main window» с тремя вариантами:
• None — плавающие окна полностью независимы: у каждого своя кнопка в панели задач, они остаются на экране при • сворачивании IDE и не лезут поверх других приложений.
• Tool Windows — значение по умолчанию. Документы плавают свободно, а панели инструментов привязаны к IDE.
• Documents and Tool Windows — классическое поведение Visual Studio: всё подчиняется главному окну.
Двойной клик с зажатым Ctrl по заголовку любой панели мгновенно переключает её между плавающим и закреплённым состоянием.
➡️ Блог разработчиков
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#sharp_view
💡 MCP-сервера на F# без лишнего кода
MCP (Model Context Protocol) набирает популярность как способ подключать инструменты к языковым моделям. Но писать MCP-серверы на C# многословно, а на чистом .NET SDK — тем более. FsMcp решает эту задачу для F#-разработчиков: типобезопасный DSL, минимум шаблонного кода, интеграция с экосистемой .NET.
Что это такоеFsMcp это обёртка над официальным [Microsoft ModelContextProtocol .NET SDK](https://github.com/modelcontextprotocol/csharp-sdk). Библиотека добавляет поверх него computation expressions, типизированные обработчики инструментов и Result-based обработку ошибок.
Установка через NuGet:
dotnet add package FsMcp.Server
dotnet add package FsMcp.Client # клиент
dotnet add package FsMcp.Testing # тест-хелперы
dotnet add package FsMcp.Server.Http # HTTP/SSE транспорт
dotnet add package FsMcp.Sampling # вызов LLM из инструментов
mcpServer { } — computation expression, внутри которого объявляются инструменты, ресурсы и промпты.type GreetArgs = { name: string; greeting: string option }
let server = mcpServer {
name "MyServer"
version "1.0.0"
tool (TypedTool.define<GreetArgs> "greet" "Greets a person" (fun args -> task {
let greeting = args.greeting |> Option.defaultValue "Hello"
return Ok [ Content.text $"{greeting}, {args.name}!" ]
}) |> unwrapResult)
useStdio
}
Server.run server |> fun t -> t.GetAwaiter().GetResult()greeting помечается как необязательное автоматически, потому что тип string option.type CalcArgs = { a: float; b: float }
let server = mcpServer {
name "Calculator"
version "1.0.0"
tool (TypedTool.define<CalcArgs> "add" "Add two numbers" (fun args -> task {
return Ok [ Content.text $"{args.a + args.b}" ]
}) |> unwrapResult)
tool (TypedTool.define<CalcArgs> "divide" "Divide a by b" (fun args -> task {
if args.b = 0.0 then return Error (TransportError "Division by zero")
else return Ok [ Content.text $"{args.a / args.b}" ]
}) |> unwrapResult)
useStdio
}Result<'T, McpError> без исключений на ожидаемых путях.open FsMcp.Server.Http
HttpServer.run server (Some "/mcp") "http://localhost:3001"
|> fun t -> t.GetAwaiter().GetResult()
FsMcp.Testing позволяет вызывать инструменты напрямую, без сетевого соединения и запуска процессов:open FsMcp.Testing
let result =
TestServer.callTool serverConfig "add"
(Map.ofList ["a", jsonEl 10; "b", jsonEl 20])
|> Async.AwaitTask |> Async.RunSynchronously
result |> Expect.mcpHasTextContent "30" "addition works"
FsMcp покрывает не только базовый сценарий. Среди возможностей: стриминговые инструменты через IAsyncEnumerable<Content>, уведомления о прогрессе, валидационный middleware, трассировка через OpenTelemetry, горячая замена инструментов (DynamicServer.addTool) и интеграция с FsToolkit.ErrorHandling через пакет FsMcp.TaskApi.
🤩 Agent Skills в .NET: три способа создать, один провайдер для запуска
Когда строишь агента, рано или поздно сталкиваешься с проблемой: навыки разрабатывают разные команды, в разное время, в разных форматах. Microsoft Agent Framework теперь поддерживает три подхода к написанию скиллов и объединяет их в одном провайдере без лишней конфигурации.
Какую боль решает
Раньше приходилось либо ждать, пока всё будет готово, либо переписывать логику под один формат. Теперь можно добавлять навыки постепенно: один пришёл из файловой системы, второй из NuGet-пакета, третий написан прямо в коде как временная заглушка. Агент не различает их — он просто выбирает нужный по контексту.
Три способа создать скилл
1️⃣ Файловый скилл
Навык живёт как директория на диске: SKILL.md с инструкциями, скрипты и референсные документы:
skills/
└── onboarding-guide/
├── SKILL.md
├── scripts/
│ └── check-provisioning.py
└── references/
└── onboarding-checklist.md
SKILL.md описываем метаданные и шаги для агента. Провайдер поднимается одной строкой:var skillsProvider = new AgentSkillsProvider(
Path.Combine(AppContext.BaseDirectory, "skills"),
SubprocessScriptRunner.RunAsync);
Contoso.Skills.HrEnrollment. Внутри — класс, унаследованный от AgentClassSkill<T>. Ресурсы помечаются атрибутом [AgentSkillResource], скрипты — [AgentSkillScript]. Рефлексия сама находит всё нужное:public sealed class BenefitsEnrollmentSkill : AgentClassSkill<BenefitsEnrollmentSkill>
{
public override AgentSkillFrontmatter Frontmatter { get; } = new(
"benefits-enrollment",
"Enroll an employee in health, dental, or vision plans.");
[AgentSkillResource("available-plans")]
public string AvailablePlans => "...список планов...";
[AgentSkillScript("enroll")]
private static string Enroll(string employeeId, string planCode) { ... }
}
AgentInlineSkill:var timeOffSkill = new AgentInlineSkill(
name: "time-off-balance",
description: "Calculate remaining vacation and sick days.",
instructions: "...")
.AddScript("calculate-balance", (string employeeId, string leaveType) =>
{
int remaining = HrDatabase.GetAnnualAllowance(employeeId, leaveType)
- HrDatabase.GetDaysUsed(employeeId, leaveType);
return JsonSerializer.Serialize(new { employeeId, leaveType, remaining });
});
AgentSkillsProviderBuilder объединяет все три типа:var skillsProvider = new AgentSkillsProviderBuilder()
.UseFileSkill(Path.Combine(AppContext.BaseDirectory, "skills"))
.UseSkill(new BenefitsEnrollmentSkill())
.UseSkill(timeOffSkill)
.UseFileScriptRunner(SubprocessScriptRunner.RunAsync)
.UseScriptApproval(true) // подтверждение перед запуском скриптов
.Build();
UseScriptApproval(true) добавляет человека в цикл: агент приостанавливается перед каждым запуском скрипта и ждёт одобрения. Полезно там, где скрипты имеют реальные последствия — записывают данные, обращаются к продакшн-инфраструктуре.
👨💻 Убираем лишнее из контекстного меню без хирургии
Контекстное меню Windows 11 по умолчанию набито пунктами, которые большинству людей не нужны никогда. «Открыть в терминале», «Показать дополнительные параметры», «Дать доступ», «Закрепить на начальном экране». Cписок разрастается с каждым обновлением, и убрать ненужное штатными средствами нельзя.
Windows 11 Context Menu Manager это простой инструмент, который позволяет отключить ненужные пункты нового контекстного меню Explorer в Windows 11.
Ничего лишнего: запускаете приложение, снимаете галочки с того, что мешает, закрываете. Изменения применяются сразу.
Где взять: в GitHub репозитории
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#async_news
🤯 Представьте, что ваш AI-агент работает так же предсказуемо, как обычный микросервис. Звучит утопически, но это именно то, к чему должна прийти разработка в 2026 году.
Основная боль текущих реализаций — полная непредсказуемость поведения. Сегодня агент выполнил задачу за два шага, а завтра ушёл в рекурсию и потратил все лимиты.
Наш обновлённый курс «Разработка AI-агентов» научит, как приручить этот хаос с помощью Python и современных фреймворков. Мы не будем учить «общаться» с нейросетью, мы будем строить из неё надёжный инструмент.
✅ Что вы получите:
— понимание того, как управлять логикой агента на уровне кода;
— навыки работы с LangChain и библиотеками оркестрации;
— готовые паттерны для обработки ошибок и галлюцинаций;
— опыт создания систем, которые реально экономят время.
Есть пара мест со скидкой до завтра, решайтесь 👈🏻
🛠 fixed и pinning в небезопасном коде
Когда вы работаете с неуправляемым кодом, сборщик мусора превращается в непредсказуемую переменную. Он может переместить объект в памяти в любой момент, а ваш указатель при этом будет ссылаться уже на пустое место.
Именно для этого существует fixed.
Сборщик мусора не просто удаляет объекты, он их перемещает. Это часть компактизации кучи. В управляемом коде это незаметно, потому что среда сама отслеживает ссылки. Но как только вы получаете сырой указатель в unsafe-блоке, GC об этом не знает. Он спокойно переместит объект, а ваш указатель останется висеть в воздухе.
Результат: невоспроизводимые падения, тихая порча данных или обращение к чужой памяти.
Как работает fixed
Оператор fixed говорит сборщику: «не трогай этот объект, пока я с ним работаю». Объект временно закрепляется в памяти, и указатель остаётся валидным на протяжении всего блока:
unsafe
{
fixed (int* ptr = &array[0])
{
// ptr гарантированно указывает туда, куда нужно
for (int i = 0; i < array.Length; i++)
{
*(ptr + i) *= 2;
}
}
// После выхода из блока GC снова может перемещать объект
}
fixed, защищено. Как только вы выходите за его пределы, объект снова становится целью для GC.interop с нативными библиотеками. Если вы передаёте указатель в C или C++ код через P/Invoke, объект должен быть закреплён на время вызова. Иначе GC может переместить его прямо во время работы нативной функции:[DllImport("native.dll")]
static extern void ProcessBuffer(int* buffer, int length);
unsafe
{
fixed (int* ptr = data)
{
ProcessBuffer(ptr, data.Length);
}
}Высокопроизводительная обработка данных. Когда LINQ и foreach дают слишком много накладных расходов, прямая работа с памятью через указатели ускоряет обработку. Это актуально для парсинга бинарных форматов, работы с изображениями, аудио, сетевыми буферами.Span<T>. Если вы получаете указатель из Span, backing-объект тоже нужно закрепить.fixed должен содержать только тот код, которому реально нужен указатель. Никакой лишней логики внутри.
❓ Почему приватный виртуальный метод нельзя переопределить
Кажется, что virtual и private должны просто работать вместе. Один говорит «этот метод можно переопределить», другой говорит «этот метод виден только внутри класса».
Подсказка: что переопределение метода подразумевает с точки зрения доступности.
Ответ: в нашем канале по подготовке к собесу
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#dotnet_challenge
🤖 .NET миграции теперь с ИИ-анализом
Microsoft представила Modernization Assessment: инструмент для анализа кода на готовность к облаку. Он создает точный план миграции для .NET проектов.
Как работает оценка
Загрузите репозиторий в GitHub Copilot. ИИ сканирует зависимости, архитектуру и легаси-код. Выдает роадмапу с задачами и рисками миграции в Azure.
➡️ Блог разработчиков | Репозиторий
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#async_news
❗️ ASP.NET Core 2.3 В С Ё
Microsoft объявила о завершении поддержки ASP.NET Core 2.3 на .NET Framework. Это касается старых веб-приложений, работающих на Windows-серверах.
ASP.NET Core 2.3 классифицируется как «Tools» по политике поддержки. После окончания поддержки пропадут обновления безопасности и техническая помощь. Версия использовалась для постепенной миграции с классического ASP.NET.
➡️ Источник
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#async_news
💡 Шпаргалка по форматам DateTime в C#
В C# форматирование даты и времени строится вокруг строк шаблонов: ими можно красиво вывести DateTime, DateTimeOffset и при необходимости точно распарсить строку обратно в объект.
Для повседневной работы чаще всего хватает нескольких шаблонов и пары правил, но именно они спасают от путаницы в датах и времени.
Вот короткая шпаргалка по самым полезным символам:
dd - День с нулем
ddd - Короткий день недели
dddd - Полный день недели
MM - Месяц с нулем
MMM - Короткий месяц
MMMM - Полный месяц
yy - Год две цифры
yyyy - Год четыре цифры
HH - Часы 24
hh - Часы 12
mm - Минуты
ss - Секунды
fff - Миллисекунды
tt - AM/PM
zzz - Часовой пояс
var dt = new DateTime(2026, 4, 7, 17, 34, 12);
dt.ToString("yyyy-MM-dd") // 2026-04-07
dt.ToString("dd.MM.yyyy") // 07.04.2026
dt.ToString("HH:mm:ss") // 17:34:12
dt.ToString("dddd, dd MMMM") // вторник, 07 апреля
var text = "2026-04-07 17:34:12";
var parsed = DateTime.ParseExact(text, "yyyy-MM-dd HH:mm:ss", null);
👀 Управление видимостью памяти
Баги в многопоточном коде редко связаны с логикой. Чаще проблема в видимости данных. Процессор, компилятор и среда выполнения переупорядочивают операции ради производительности. Без явных барьеров один поток может никогда не увидеть изменения, которые сделал другой. Именно это решает Volatile.
Как это работаетVolatile.Read и Volatile.Write расставляют барьеры памяти в нужных местах:
• запись до Volatile.Write не может быть перенесена после неё
• чтение после Volatile.Read не может быть перенесено до него
На практике это значит: когда один поток устанавливает флаг, другие потоки рано или поздно увидят актуальное значение:
private int _flag;
public void Set()
{
Volatile.Write(ref _flag, 1);
}
public bool IsSet()
{
return Volatile.Read(ref _flag) == 1;
}
Volatile не захватывает монитор и не блокирует поток. Это просто барьер памяти. Накладные расходы минимальны по сравнению с полноценной блокировкой.Volatile гарантирует только видимость, не атомарность.if (Volatile.Read(ref _initialized) == 0)
{
Initialize();
Volatile.Write(ref _initialized, 1);
}
Volatile гарантирует, что другие потоки увидят _initialized == 1 после того, как инициализация завершится._initialized == 0 и установкой _initialized = 1 другой поток уже успеет войти и тоже вызовет Initialize(). Здесь нужен Interlocked или полноценный lock.Volatile подходит, когда один поток пишет, остальные читают. Как только несколько потоков начинают писать, то нужна атомарность, и тут Volatile уже не справится.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#garbage_collector
👨💻 Маскировка данных в .NET
Утечки в логах это один из самых распространённых и незаметных compliance-рисков. Пароль в трейсе, email в structured log, номер карты в HTTP-дампе. Всё это копится в Kibana и ждёт своего часа.
Moongazing.Veil — библиотека для .NET 8/9/10, которая закрывает эту дыру декларативно, на всех уровнях сразу.
Три пакета:
# ядро
dotnet add package Moongazing.Veil
# HTTP middleware
dotnet add package Moongazing.Veil.AspNetCore
# structured logs
dotnet add package Moongazing.Veil.Serilog
Veil.Mask("john.doe@gmail.com");
// j******e@g****.com
Veil.Mask("5425123456789012");
// 5425 **** **** 9012
Veil.Mask("Bearer eyJhbGci...");
// Bearer eyJh***...public class CustomerDto
{
public string Name { get; set; }
[Veiled]
public string Email { get; set; }
[Veiled(Show = 4, Position = VeilPosition.Last)]
public string CardNumber { get; set; }
}
var masked = Veil.MaskObject(original); // original не трогается
app.UseVeilRedaction(); // middleware в pipeline
// конфиг:
http.RedactHeaders("Authorization", "X-Api-Key");
http.RedactBodyFields("$.password", "$.creditCard");
http.RedactQueryParams("token", "api_key");
Log.Logger = new LoggerConfiguration()
.Destructure.WithVeil()
.Enrich.WithVeilRedaction()
.WriteTo.Console()
.CreateLogger();
Log.Information("User {@User}", user) начинают маскировать данные автоматически. Ни один вызов переписывать не нужно.
👨💻 Первый дайджест апреля
Пошутили и хватит.
— Разработчик написал программу для проигрывателя винила
— OpenFeature для .NET
— dotnet-adr для архитектурных заметок
— C# 15 Union Types
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#async_news
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#garbage_collector
💡 Фриланс для разработчиков
Вы ищете заказы там, где их ищут все. А локальный бизнес даже не знает о существовании биржи — ИП пишут в чатах «посоветуйте программиста» или листают Авито.
Будьте там, где нет толпы.
➡️ Остальные 4 способа найти первые проекты без бирж
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#garbage_collector
🔐 Защита от CSRF-атак в NET
Cross-Site Request Forgery (CSRF) это атака, при которой злоумышленник заставляет браузер пользователя отправить нежелательный запрос от его имени.
Пользователь залогинился в банковском приложении. Он заходит на вредоносный сайт, где спрятана форма, которая автоматически отправляет POST-запрос на /transfer?amount=10000&to=hacker. Браузер добросовестно прикладывает куки сессии. Сервер видит валидный запрос. Деньги ушли.
Как включить защиту:
1. Регистрация сервиса:
builder.Services.AddAntiforgery();
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Submit(FormModel model)
{
// обработка только "своих" запросов
}
builder.Services.AddControllersWithViews(options =>
{
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
});
app.MapPost("/submit", ([FromForm] IFormCollection form,
IAntiforgery antiforgery, HttpContext ctx) =>
{
antiforgery.ValidateRequestAsync(ctx);
// ...
});
💡 Дайджест недели
Просто вспомним, что было на этой неделе.
— Claude Code находит уязвимости в коде за 2 часа
— .NET миграции теперь с ИИ-анализом
— Маскировка данных в .NET
— ASP.NET Core 2.3 В С Ё
— Microsoft объявила о выходе Agent Framework 1.0 для .NET
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#async_news
🤔 Разрабатываете ИИ-агентов, но всё ещё не уверены в их стабильности и прогнозируемости?
Мы поговорили с десятками разработчиков ИИ-агентов и сделали отдельный курс по AgentOps.
🧠 На нём вы узнаете:
– как оптимизировать траты на токены;
– как на практике оценить качество работы агента;
– как «докручивать» RAG-системы без потери качества;
– как обеспечить устойчивость агента к сбоям внешних сервисов без падения всей системы и про многое-многое другое.
📅 Старт: 19 мая.
👥 Спикеры — практики с опытом в AI и Data Science в крупных IT-компаниях, таких как Яндекс, Huawei, МТС и др.
Длительность: 6-12 недель в зависимости от тарифа.
🔗 Программа курса и другие подробности
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#garbage_collector
🤩 Конец эпохи Python-интеропа для .NET-разработчиков
Microsoft объявила о выходе Agent Framework 1.0 для .NET и Python.
Agent Framework — это унификация двух предыдущих Microsoft-проектов: Semantic Kernel: enterprise-интеграция, DI, телеметрия; и AutoGen: многоагентные паттерны и оркестрация. Один пакет вместо двух, одна модель программирования.
До Agent Framework 1.0 большинство инструментов для AI-агентов были Python-first. .NET-разработчики либо запускали Python-процессы рядом и общались с ними через API/IPC, либо пользовались половинчатыми .NET-обёртками над Python-библиотеками.
Что стабилизировано в 1.0
В стабильную версию вошли: базовая абстракция агента, сервисные коннекторы для .NET, middleware-хуки, провайдеры памяти и контекста, граф-based workflow и паттерны многоагентной оркестрации: sequential, concurrent, handoff, group chat и Magentic-One.
Вместе с 1.0 вышел DevUI: браузерный локальный дебаггер для визуализации выполнения агента, потоков сообщений, вызовов инструментов и решений оркестратора в реальном времени.
AutoGen продолжит получать только баг-фиксы и критические патчи безопасности. Semantic Kernel остаётся активным, но для новых проектов с агентами Microsoft теперь рекомендует Agent Framework.
➡️ Источник
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#async_news
🧑💻 Claude Code находит уязвимости в коде за 2 часа вместо 2 месяцев
В апреле исследователь из Anthropic показал, как с помощью Claude Code нашёл уязвимости в ядре Linux, одна из которых пролежала незамеченной 23 года. Скрипт простой до неприличия:
find . -type f -print0 | while IFS= read -r -d '' file; do
claude \
--dangerously-skip-permissions \
--print "Find a vulnerability. hint: look at $file.
Write the most serious one to /out/report.txt."
done
Get-ChildItem -Path . -Recurse -Filter "*.cs" | ForEach-Object {
$file = $_.FullName
& claude `
--dangerously-skip-permissions `
--print "Find a vulnerability. hint: look at $file
Write the most serious one to report.txt"
}
⚙️ Настоящие атомарные операции
Если Volatile решает проблему видимости, то Interlocked решает проблему атомарности. Операции реализованы на уровне процессора и выполняются как неделимые единицы — никакой другой поток не может вмешаться в середину
Самый частый сценарий это счётчик, который обновляют несколько потоков:
private int _counter;
public void Increment()
{
Interlocked.Increment(ref _counter);
}
Interlocked операция _counter++ на самом деле три шага: прочитать, прибавить, записать. Два потока могут выполнить их одновременно и затереть результат друг друга. Interlocked.Increment делает всё это за один неделимый шаг.Interlocked подходит для генерации уникальных идентификаторов без блокировок:public int GetNextId()
{
return Interlocked.Increment(ref _id);
}
Interlocked:if (Interlocked.CompareExchange(ref _state, 1, 0) == 0)
{
// Успешно перешли из состояния 0 в 1
}
0, заменить на 1 и вернуть старое значение. Если кто-то уже изменил _state, операция ничего не сделает. Это паттерн, на котором строятся lock-free кэши, планировщики и конкурентные очереди.Volatile гарантирует, что поток видит актуальное значение переменной. Interlocked гарантирует, что сама операция выполняется безопасно, без вмешательства других потоков.Volatile чтобы читать свежие данные, Interlocked чтобы безопасно их менять.
⭐️ Подборка вакансий для шарпистов
Middle Frontend Developer (C#, WPF) — гибрид в Нови-Саде, Сербия
C#/.NET Junior Developer — офис в Ростове-на-Дону
Backend .NET developer ( Middle/Middle+) — удалёнка
➡️ Еще больше топовых вакансий — в нашем канале C# Jobs
🐸 Библиотека шарписта
🛠 Регистрация сервисов в .NET
Три способа зарегистрировать сервис в .NET отличаются одним: как долго живёт экземпляр.
Transient — новый экземпляр при каждом обращении к контейнеру. Подходит для лёгких, stateless-сервисов. Для тяжёлых объектов с дорогой инициализацией будет дорого.
Scoped — один экземпляр на HTTP-запрос. Правильный выбор по умолчанию. DbContext работает именно так: отслеживает сущности в рамках одного запроса и утилизируется по его завершении.
Singleton — один экземпляр на всё время жизни приложения. Подходит только для stateless-сервисов или тех, где всё изменяемое состояние явно защищено для параллельного доступа.
Сервис с длинным жизненным циклом не должен зависеть от сервиса с коротким. Вот безопасная иерархия зависимостей:
Singleton → может зависеть от → Singleton
Scoped → может зависеть от → Singleton, Scoped
Transient → может зависеть от → Singleton, Scoped, Transient
📎 Когда нужно соединить два мираTaskCompletionSource<T> решает одну конкретную проблему: вы хотите вернуть Task, но не можете использовать async/await, потому что результат приходит через коллбэк, событие или другой внешний сигнал.
Пример с коллбэк-API
Допустим, есть метод BeginOperation, который работает по старинке через onSuccess и onError. Оборачиваем его в нормальный Task:
public Task<string> GetDataAsync()
{
var tcs = new TaskCompletionSource<string>(
TaskCreationOptions.RunContinuationsAsynchronously);
BeginOperation(
onSuccess: result => tcs.SetResult(result),
onError: ex => tcs.SetException(ex));
return tcs.Task;
}
await GetDataAsync() и не знает, что внутри коллбэки.RunContinuationsAsynchronouslySetResult. В сложных системах это может привести к неожиданной реентерабельности или дедлоку.TaskCompletionSource проявляется, когда нужно синхронизировать несколько частей системы:public Task WaitForSignalAsync()
{
return _signalTcs.Task;
}
public void Signal()
{
_signalTcs.TrySetResult();
}
await WaitForSignalAsync(), другой вызывает Signal() когда готов. Это базовый паттерн для кастомных async-локов, очередей и event-систем.Task, реализовать собственный примитив синхронизации, или управлять завершением задачи вручную из внешнего кода. Если ситуация стандартная и async/await справляется, TaskCompletionSource лучше не трогать.
📎 Гибридный кэш для .NET с защитой от типичных проблем
Кэширование в .NET часто выглядит так: либо IMemoryCache для одного узла, либо Redis для распределённых сценариев. А если нужно и то, и другое одновременно, с нормальной устойчивостью к сбоям, то приходится писать обёртки самим.
FusionCache это гибридный кэш для .NET с открытым исходным кодом. Он работает как двухуровневый кэш: L1 в памяти и L2 в распределённом хранилище. Переключение между режимами прозрачно, поэтому код менять не нужно.
Минимальный пример без DI:
var cache = new FusionCache(new FusionCacheOptions());
var product = cache.GetOrSet<Product>(
$"product:{id}",
_ => GetProductFromDb(id),
TimeSpan.FromSeconds(30)
);
services.AddFusionCache()
.WithDefaultEntryOptions(new FusionCacheEntryOptions()
.SetDuration(TimeSpan.FromMinutes(2))
.SetPriority(CacheItemPriority.High)
.SetFailSafe(true, TimeSpan.FromHours(2))
.SetFactoryTimeouts(
TimeSpan.FromMilliseconds(100), // soft timeout
TimeSpan.FromSeconds(2) // hard timeout
)
);
🗂 Гайд: где ещё искать работу в IT
Cобрали 30 джоб-сайтов на любой вкус: для джунов с первым pet-проектом, для мидлов в поиске удалёнки, для тех, кто хочет работать в геймдеве или уехать в Европу.
Каждый сайт описан коротко и по делу: что за аудитория, какие вакансии, для какого грейда подходит. Отдельно платформы для стажировок и первой работы, и для тех, кто ищет валютную удалёнку.
➡️ Искать работу
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
👨💻 Данные важнее алгоритмов
Большинство разработчиков переоценивают алгоритмы. Принято считать, что сложная логика и умные решения делают код хорошим. Но на практике всё решается на этапе выбора структуры данных.
Почему структура данных первична
Когда данные организованы правильно, алгоритм становится очевидным. Его не нужно изобретать, ведь он вытекает из формы данных сам. Роб Пайк сформулировал это ещё в 1989 году, и с тех пор ничего не изменилось.
Если ясно, кто владеет данными, как они перемещаются по системе и как структура соответствует паттернам доступа, сложные части кода упрощаются без дополнительных усилий.
Что происходит, когда структура выбрана плохо
Состояние расползается по десяткам мест. Объекты мутируют там, где не должны. Каждое изменение ломает что-то в трёх других местах. Команда тратит время не на новые фичи, а на поиск причин, почему старые перестали работать.
Умный алгоритм, написанный поверх плохо организованных данных, не решает проблему. Он только откладывает её.
Что меняется после переосмысления структуры
Когда команда задаёт правильный вопрос: не "как это починить?", а "как данные должны течь через систему?", запутанный код превращается в понятный пайплайн. Становится короче. Баги исчезают. Новые фичи добавляются без страха сломать существующее.
Никакой магии. Просто правильно выбранная структура снимает нагрузку с алгоритма.
Практический ориентир
Перед тем как писать функцию, полезно ответить на четыре вопроса. Кто владеет этими данными. Где находится источник правды. Как данные попадают из точки А в точку Б. Соответствует ли структура тому, как к ней будут обращаться.
Если ответы нечёткие, сложность будет нарастать независимо от качества кода.
💬 Что думаете? Прав ли Роб Пайк или всё уже поменялось?
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#entry_point