Библиотека шарписта | C#, F#, .NET, ASP.NET
22K subscribers
2.74K photos
41 videos
85 files
5.11K links
Все самое полезное для C#-разработчика в одном канале.

По рекламе: @proglib_adv

Учиться у нас: https://proglib.io/w/b60af5a4

Для обратной связи: @proglibrary_feeedback_bot

РКН: https://gosuslugi.ru/snet/67a5c81cdc130259d5b7fead
Download Telegram
👨‍💻 Убираем лишнее из контекстного меню без хирургии

Контекстное меню Windows 11 по умолчанию набито пунктами, которые большинству людей не нужны никогда. «Открыть в терминале», «Показать дополнительные параметры», «Дать доступ», «Закрепить на начальном экране». Cписок разрастается с каждым обновлением, и убрать ненужное штатными средствами нельзя.

Windows 11 Context Menu Manager это простой инструмент, который позволяет отключить ненужные пункты нового контекстного меню Explorer в Windows 11.

Ничего лишнего: запускаете приложение, снимаете галочки с того, что мешает, закрываете. Изменения применяются сразу.

Где взять: в GitHub репозитории

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#async_news
Please open Telegram to view this post
VIEW IN TELEGRAM
👍52
🔐 Защита от CSRF-атак в NET

Cross-Site Request Forgery (CSRF) это атака, при которой злоумышленник заставляет браузер пользователя отправить нежелательный запрос от его имени.

Пользователь залогинился в банковском приложении. Он заходит на вредоносный сайт, где спрятана форма, которая автоматически отправляет POST-запрос на /transfer?amount=10000&to=hacker. Браузер добросовестно прикладывает куки сессии. Сервер видит валидный запрос. Деньги ушли.

Как включить защиту:

1. Регистрация сервиса:
builder.Services.AddAntiforgery();


2. Защита конкретного эндпоинта:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Submit(FormModel model)
{
// обработка только "своих" запросов
}


3. Глобальная защита через фильтр:
builder.Services.AddControllersWithViews(options =>
{
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
});


Minimal API — вручную:
app.MapPost("/submit", ([FromForm] IFormCollection form,
IAntiforgery antiforgery, HttpContext ctx) =>
{
antiforgery.ValidateRequestAsync(ctx);
// ...
});


CSRF-защита критична для любых форм, меняющих состояние: оплата, смена пароля, удаление данных.

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
8
🤩 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);


2️⃣ Классовый скилл из NuGet

Команда HR-систем публикует 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) { ... }
}


3️⃣ Инлайн-скилл

Нужный навык ещё не вышел, а ждать нельзя. Пишем прямо в коде через 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 });
});


Когда NuGet-пакет выйдет, то просто убираем строку из билдера. Агент ничего не заметит.

Собираем всё вместе

AgentSkillsProviderBuilder объединяет все три типа:
var skillsProvider = new AgentSkillsProviderBuilder()
.UseFileSkill(Path.Combine(AppContext.BaseDirectory, "skills"))
.UseSkill(new BenefitsEnrollmentSkill())
.UseSkill(timeOffSkill)
.UseFileScriptRunner(SubprocessScriptRunner.RunAsync)
.UseScriptApproval(true) // подтверждение перед запуском скриптов
.Build();


UseScriptApproval(true) добавляет человека в цикл: агент приостанавливается перед каждым запуском скрипта и ждёт одобрения. Полезно там, где скрипты имеют реальные последствия — записывают данные, обращаются к продакшн-инфраструктуре.

Подход удобен тем, что каждый шаг независим. Не нужно согласовывать форматы между командами или держать всё в одном репозитории. Файловые скиллы хорошо подходят для контента, который часто меняется. Классовые — для переиспользуемой логики, которую удобно поставлять как пакет. Инлайновые — для временных заглушек и случаев, когда скилл нужно генерировать динамически из данных.

➡️ Источник | Документация | Примеры на GitHub

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
9
💡 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, внутри которого объявляются инструменты, ресурсы и промпты.

Инструмент принимает F#-запись как входные данные, JSON Schema генерируется автоматически через TypeShape:
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> без исключений на ожидаемых путях.

HTTP-транспорт

Если нужен не stdio, а HTTP/SSE:
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.

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
3🤩3👍1🤔1
💡 Фриланс для разработчиков

Вы ищете заказы там, где их ищут все. А локальный бизнес даже не знает о существовании биржи — ИП пишут в чатах «посоветуйте программиста» или листают Авито.

Будьте там, где нет толпы.

➡️ Остальные 4 способа найти первые проекты без бирж

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
💻 Плавающие окна в 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
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥124
🚀 .NET 11 Preview 3

Вышел третий превью-релиз .NET 11. Обновления затронули рантайм, SDK, библиотеки, ASP.NET Core, EF Core и .NET MAUI. Разберём главное.

Библиотеки

System.Text.Json получил больше контроля над именованием полей и обработкой значений по умолчанию. Алгоритм сжатия Zstandard переехал в System.IO.Compression — теперь он часть стандартной библиотеки, а не отдельный пакет. ZIP-чтение добавило валидацию CRC32. Regex теперь корректно обрабатывает все Unicode-последовательности переноса строки.

Рантайм

Runtime async больше не требует явного opt-in через атрибут — функция стала стабильнее и готовится к основному потоку. JIT-компилятор улучшил оптимизацию switch-выражений, проверок границ массивов и операций приведения типов. Для WebAssembly добавлена поддержка WebCIL и улучшена отладка в браузере.

SDK

dotnet run теперь принимает переменные окружения прямо из командной строки:
dotnet run -e MY_VAR=value


Solution-фильтры можно редактировать через CLI без открытия IDE. Файловые приложения теперь можно разбивать на несколько файлов. dotnet watch получил поддержку Aspire, восстановление после краша и улучшения для Windows Desktop.

C#

Добавлена начальная поддержка типа union это размеченные объединения в духе F# или Rust. Пока preview, но направление понятное.

ASP.NET Core

Поддержка Zstandard для сжатия ответов и распаковки запросов. Компонент Virtualize в Blazor теперь адаптируется к элементам с переменной высотой в рантайме. HTTP/3 начинает обрабатывать запросы раньше — меньше задержек на старте соединения.

Entity Framework Core

ChangeTracker.GetEntriesForState() позволяет получать записи по состоянию без лишнего обнаружения изменений. DbContext теперь умеет удалять провайдеры и добавлять пулы фабрик. SQL-генератор стал убирать лишние JOIN-ы, а для SQL Server добавлены JSON API.

Контейнеры

Образы .NET на mcr.microsoft.com теперь подписываются. Можно верифицировать их подлинность перед запуском.

.NET MAUI

Maps API получил кластеризацию маркеров, стилизацию и расширенное API взаимодействия. LongPressGestureRecognizer теперь встроен в платформу. Добавлена поддержка Android 17 / API 37 в режиме preview.

➡️ Источник

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#async_news
Please open Telegram to view this post
VIEW IN TELEGRAM
👍86🌚1
🤖 Подборка вакансий для шарпистов

Team Lead C# — от 350 000 ₽ в офис или на удалёнку в Москве

Программист C#/С++/. NET (Middle) — до 400 000 ₽, офис в Москве

Backend .NET developer ( Middle/Middle+) — удалёнка

➡️ Еще больше топовых вакансий — в нашем канале C# Jobs

🐸 Библиотека шарписта
Please open Telegram to view this post
VIEW IN TELEGRAM
😊 Маппинг объектов без рефлексии и без оверхеда

Маппинг между моделями это одна из самых скучных вещей в C#-разработке. AutoMapper решает проблему, но тянет рефлексию в рантайме. FreakyKit.Forge генерирует весь маппинг-код на этапе компиляции через Roslyn source generators.

Вы объявляете partial-метод, библиотека генерирует его реализацию при сборке. Никакой рефлексии, никаких зависимостей в рантайме.

Установка:
<ItemGroup>
<PackageReference Include="FreakyKit.Forge.Generator" Version="1.0.0" />
<PackageReference Include="FreakyKit.Forge.Analyzers" Version="1.0.0" />
</ItemGroup>


Как это работает

Помечаем статический класс атрибутом [Forge] и объявляем метод без тела:
using FreakyKit.Forge;

public class Person { public string Name { get; set; } public int Age { get; set; } }
public class PersonDto { public string Name { get; set; } public int Age { get; set; } }

[Forge]
public static partial class PersonForges
{
public static partial PersonDto ToDto(Person source);
}


При компиляции Forge генерирует реализацию:
public static partial PersonDto ToDto(Person source)
{
var __result = new PersonDto();
__result.Name = source.Name;
__result.Age = source.Age;
return __result;
}


Вызов выглядит так:
var dto = PersonForges.ToDto(person);


Возможности

Базовый сценарий это маппинг по совпадению имён. Но библиотека умеет больше.

Переименование полей через [ForgeMap]:
public class Source { [ForgeMap("Name")] public string FirstName { get; set; } }
public class Dest { public string Name { get; set; } }


Вложенные объекты с AllowNestedForging:
[Forge]
public static partial class PersonForges
{
public static partial AddressDto ToAddressDto(Address source);

[ForgeMethod(AllowNestedForging = true)]
public static partial PersonDto ToDto(Person source);
}
// Генерирует: __result.Home = ToAddressDto(source.Home);


Коллекции обрабатываются автоматически — List<T>, T[], ImmutableArray<T> и другие. Если типы элементов отличаются, достаточно включить AllowNestedForging.

Обновление существующего объекта вместо создания нового — метод с двумя параметрами и void:
public static partial void Update(Person source, PersonDto existing);
// Генерирует: existing.Name = source.Name; existing.Age = source.Age;


Строгий режим для отлова расхождений при рефакторинге:
[ForgeMethod(StrictMapping = true)]
public static partial PersonDto ToDto(Person source);
// Если добавить поле в Source или Dest — компилятор выдаст ошибку, не предупреждение


Тип-конвертеры для несовместимых типов:
[ForgeConverter]
public static string ConvertDateTime(DateTime value) => value.ToString("yyyy-MM-dd");
// Forge сам найдёт подходящий конвертер и применит его при маппинге


Диагностика

Пакет FreakyKit.Forge.Analyzers добавляет 35 диагностических правил. Ошибки в объявлениях маппингов вы увидите прямо в редакторе при сборке, а не в рантайме.

➡️ Репозиторий

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
11🤔8
🔄 Апрельские обновления безопасности 2026 .NET

Microsoft выпустила плановые обновления для .NET 8, 9, 10 и .NET Framework. В этот раз пакет преимущественно про безопасность, ведь закрыто шесть CVE.

Три типа уязвимостей затронули сразу несколько версий:

CVE-2026-23666 — отказ в обслуживании (DoS) в .NET Framework 3.0, 4.6.2–4.8.1

CVE-2026-26171 — обход защитного механизма. Затронуты .NET 8, 9, 10 и .NET Framework 2.0, 4.6.2–4.8.1

CVE-2026-32178 — удалённое выполнение кода (RCE). Затронуты .NET 8, 9, 10 и .NET Framework 2.0, 4.6.2–4.8.1

CVE-2026-32203 — DoS в .NET 8, 9, 10 и .NET Framework 2.0, 4.6.2–4.8.1

CVE-2026-32226 — DoS только в .NET Framework 2.0, 4.6.2–4.8.1

CVE-2026-33116 — RCE в .NET 8, 9, 10

➡️ Источник

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#async_news
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
🤖 Ваш ИИ-агент съедает бюджет на токены и падает при сбоях API?

Пора переходить на новый уровень. Открыли продажи курса по AgentOps — управлению ИИ-агентами в рабочих процессах.

Рынок требует инженеров, которые умеют:

• Контролировать метрики и качество ответов;
• Эффективно работать с RAG-архитектурой;
• Строить системы, готовые к реальным нагрузкам.

Обучение займет 6-12 недель под руководством практиков с опытом в AI и Data Science в крупных IT-компаниях, таких как Яндекс, Сбер, МТС, Huawei, Raft и др.

🎁 Можно подождать, пока про AgentOps начнут говорить все. Или зайти сейчас — НА 30% ДЕШЕВЛЕ!

Работа с AI начинается с систем.
Системы — с AgentOps.


🔥 Забрать скидку и изучить программу.
😁2
🔒 Три строки, которые закрывают базовые уязвимости

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

Как добавить

Подключаем middleware в Program.cs:
app.Use(async (context, next) =>
{
context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
context.Response.Headers.Add("X-Frame-Options", "DENY");
context.Response.Headers.Add("X-XSS-Protection", "1; mode=block");
await next();
});


Этот блок нужно поставить до app.UseRouting() и других middleware, иначе часть ответов уйдёт без заголовков.

Что делает каждый заголовок

X-Content-Type-Options: nosniff запрещает браузеру угадывать тип содержимого. Без него браузер может решить, что загруженный текстовый файл — это скрипт, и выполнить его. Атака называется MIME sniffing.

X-Frame-Options: DENY запрещает встраивать страницу в <iframe>. Защищает от clickjacking — когда злоумышленник накладывает прозрачный iframe поверх своего сайта и перехватывает клики пользователя:
// запрет для всех
context.Response.Headers.Add("X-Frame-Options", "DENY");

// или разрешить только своему домену
context.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN");


X-XSS-Protection: 1; mode=block включает встроенный XSS-фильтр в старых браузерах. Если атака обнаружена — страница блокируется целиком, а не фильтруется частично. Современные браузеры опираются на Content-Security-Policy, а этот заголовок оставлен для совместимости со старыми версиями IE и Edge.

Что добавить ещё

Три заголовка выше это минимум. Для полноценной защиты стоит также настроить:

Content-Security-Policy — белый список источников скриптов, стилей и медиа. Самый мощный инструмент против XSS:
context.Response.Headers.Add(
"Content-Security-Policy",
"default-src 'self'; script-src 'self'; style-src 'self'"
);


Referrer-Policy — контролирует, какой URL браузер передаёт при переходе с вашей страницы:
context.Response.Headers.Add("Referrer-Policy", "strict-origin-when-cross-origin");


Permissions-Policy — ограничивает доступ к браузерным API: камера, микрофон, геолокация:
context.Response.Headers.Add(
"Permissions-Policy",
"camera=(), microphone=(), geolocation=()"
);


Проверка

После деплоя проверить заголовки можно на securityheaders.com — сервис покажет, что выставлено, что отсутствует и насколько это критично. Или через DevTools: вкладка Network, любой запрос, раздел Response Headers.

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14🔥63
👨‍💻 Singleton vs Service Locator в Unity

Когда проект перерастает пару сцен, рано или поздно встаёт вопрос: как организовать доступ к общим сервисам: звуку, аналитике, данным игрока?

Два самых распространённых ответа это Singleton и Service Locator. Разберём, чем они отличаются и когда какой уместен.

Singleton

Паттерн знаком почти каждому Unity-разработчику. Один экземпляр класса на всё приложение, доступный из любой точки кода:
public class AudioManager : MonoBehaviour
{
public static AudioManager Instance { get; private set; }

private void Awake()
{
if (Instance != null)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject);
}

public void PlaySound(AudioClip clip) { /* ... */ }
}


Плюсы:

+ Просто реализовать
+ Мгновенный доступ без регистрации
+ Подходит для небольших проектов

Минусы:

- Жёсткая связность: классы напрямую зависят от конкретной реализации
- Тестировать сложно — нельзя подменить AudioManager на заглушку
- При разрастании проекта Instance начинают тянуть отовсюду, и следить за зависимостями становится тяжело

Service Locator

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

Сначала создаём интерфейс:
public interface IAudioService
{
void PlaySound(AudioClip clip);
}


Реализация сервиса:
public class AudioManager : MonoBehaviour, IAudioService
{
public void PlaySound(AudioClip clip) { /* ... */ }
}


Сам локатор:
public static class ServiceLocator
{
private static readonly Dictionary<Type, object> services = new();

public static void Register<T>(T service)
{
services[typeof(T)] = service;
}

public static T Get<T>()
{
if (services.TryGetValue(typeof(T), out var service))
return (T)service;

throw new Exception($"Сервис {typeof(T)} не зарегистрирован");
}
}


Плюсы:

+ Код зависит от интерфейса, а не от реализации
+ Легко подменить сервис на заглушку в тестах
+ Зависимости явно регистрируются в одном месте

Минусы:

- Если забыть зарегистрировать сервис, то получим исключение в рантайме, а не ошибку компиляции
- Чуть больше кода на старте
- Без дисциплины реестр может превратиться в свалку

Когда что выбирать

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

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

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

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
Что выведет код с картинки

💡 Подсказка: обратите внимание на конструкцию when. Это не просто синтаксический сахар.

➡️ А ответ тут

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#dotnet_challenge
Please open Telegram to view this post
VIEW IN TELEGRAM