fa
Feedback
Библиотека шарписта | C#, F#, .NET, ASP.NET

Библиотека шарписта | C#, F#, .NET, ASP.NET

رفتن به کانال در Telegram

Все самое полезное для C#-разработчика в одном канале. По рекламе: @proglib_adv Учиться у нас: https://proglib.io/w/b60af5a4 Для обратной связи: @proglibrary_feeedback_bot РКН: https://gosuslugi.ru/snet/67a5c81cdc130259d5b7fead

نمایش بیشتر

📈 تحلیل کانال تلگرام Библиотека шарписта | C#, F#, .NET, ASP.NET

کانال Библиотека шарписта | C#, F#, .NET, ASP.NET (@csharpproglib) در بخش زبانی روسی بازیگری فعال است. در حال حاضر جامعه شامل 21 873 مشترک است و جایگاه 6 218 را در دسته فناوری و برنامه‌ها و رتبه 30 852 را در منطقه روسيا دارد.

📊 شاخص‌های مخاطب و پویایی

از زمان ایجاد در невідомо، پروژه رشد سریعی داشته و 21 873 مشترک جذب کرده است.

بر اساس آخرین داده‌ها در تاریخ 09 ژوئن, 2026، کانال فعالیت پایداری دارد. در ۳۰ روز گذشته تغییر اعضا برابر -88 و در ۲۴ ساعت گذشته برابر -7 بوده و همچنان دسترسی گسترده‌ای حفظ شده است.

  • وضعیت تأیید: تأیید نشده
  • نرخ تعامل (ER): میانگین تعامل مخاطب 11.86% است و در ۲۴ ساعت نخست پس از انتشار، محتوا معمولاً 7.09% واکنش نسبت به کل مشترکان کسب می‌کند.
  • دسترسی پست‌ها: هر پست به طور میانگین 2 594 بازدید دریافت می‌کند. در اولین روز معمولاً 1 550 بازدید جمع‌آوری می‌شود.
  • واکنش‌ها و تعامل: مخاطبان به‌طور فعال حمایت می‌کنند؛ میانگین واکنش به هر پست 9 است.
  • علایق موضوعی: محتوا بر موضوعات کلیدی مانند .net, шарписта, навигация, await, string تمرکز دارد.

📝 توضیح و سیاست محتوایی

نویسنده این فضا را محل بیان دیدگاه‌های شخصی توصیف می‌کند:
Все самое полезное для C#-разработчика в одном канале. По рекламе: @proglib_adv Учиться у нас: https://proglib.io/w/b60af5a4 Для обратной связи: @proglibrary_feeedback_bot РКН: https://gosuslugi.ru/snet/67a5c81cdc130259d5b7fead

به لطف به‌روزرسانی‌های پرتکرار (آخرین داده در تاریخ 10 ژوئن, 2026)، کانال همواره به‌روز و دارای دسترسی بالاست. تحلیل‌ها نشان می‌دهد مخاطبان به‌طور فعال با محتوا تعامل دارند و آن را به نقطه اثرگذاری مهم در دسته فناوری و برنامه‌ها تبدیل کرده‌اند.

21 873
مشترکین
-724 ساعت
-187 روز
-8830 روز
آرشیو پست ها
🗓 В следующий вторник (28.04) в 19:00 встречаемся в онлайне. Тема:
Как эффективно управлять контекстным окном LLM в мультиагентных системах и не сливать бюджет на токены
В кружке Кирилл рассказал, какие именно подходы будем разбирать. 👉 Занять место на вебинаре

پیام ویدیو00:48

🧑‍💻 ИИ найдёт слабые места в вашем резюме Лайфхак, который используют единицы: найти профили людей с нужным тайтлом, которы
🧑‍💻 ИИ найдёт слабые места в вашем резюме Лайфхак, который используют единицы: найти профили людей с нужным тайтлом, которые уже устроились и проанализировать их через ИИ. 200–300 профилей дадут чёткую картину: в чём вы сильнее рынка, где сливаете на фоне конкурентов и что исправить в резюме прямо сейчас. ➡️ Это лишь один из 5 промптов из статьи 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека шарписта

🗺 Группировка меток в картах MAUI Если на карте десятки или сотни меток, они сливаются в непроходимую кашу. В .NET MAUI 11 P
🗺 Группировка меток в картах MAUI Если на карте десятки или сотни меток, они сливаются в непроходимую кашу. В .NET MAUI 11 Preview 3 появилась кластеризация меток из коробки на Android и iOS/Mac Catalyst. Как включить Одно свойство:
<maps:Map IsClusteringEnabled="True" />
Ближайшие метки автоматически группируются в кластер с числом внутри. При увеличении масштаба кластер раскрывается до отдельных пинов. Разные группы кластеров Если нужно, чтобы кофейни кластеризовались отдельно от парков, используем ClusteringIdentifier на Pin:
map.Pins.Add(new Pin
{
    Label = "Pike Place Coffee",
    Location = new Location(47.6097, -122.3331),
    ClusteringIdentifier = "coffee"
});

map.Pins.Add(new Pin
{
    Label = "Occidental Square",
    Location = new Location(47.6064, -122.3325),
    ClusteringIdentifier = "parks"
});
Пины с одинаковым идентификатором группируются вместе. С разными — образуют независимые кластеры, даже если географически рядом. Пины без идентификатора попадают в общую группу по умолчанию. Обработка нажатия на кластер При тапе на кластер срабатывает событие ClusterClicked:
map.ClusterClicked += async (sender, e) =>
{
    string names = string.Join("\n", e.Pins.Select(p => p.Label));
    await DisplayAlert(
        $"Кластер ({e.Pins.Count} меток)",
        names,
        "OK");

    // Отменить стандартное приближение к кластеру:
    // e.Handled = true;
};
В ClusterClickedEventArgs три поля: • Pins — список пинов в кластере • Location — географический центр • Handled — если true, стандартное поведение (зум к кластеру) отменяется. Платформенные детали На Android кластеризация работает через собственный алгоритм на основе сетки, пересчитывается при смене масштаба. Сторонние зависимости не нужны. На iOS и Mac Catalyst используется нативный MKClusterAnnotation из MapKit — анимации и поведение платформенные. Пример с кластеризацией уже есть в репозитории, страница Clustering в проекте MapDemo. ➡️ Блог разработчиков 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека шарписта #il_люминатор

💡 Фильтрация коллекции по типу Когда в коллекции хранятся объекты разных типов, обычный перебор с ручной проверкой is или as быстро превращается в шаблонный код. OfType<T> решает это в одну строку. Пример:
var mixedList = new List<object> { 1, "hello", 2.5, "world", 3, true };

var stringsOnly = mixedList.OfType<string>();

// Результат: hello, world
Из шести элементов метод выбирает только строки. Числа, булево значение и double игнорируются. Как это работает под капотом OfType<T> использует оператор is для каждого элемента. Примерно так выглядит его реализация:
public static IEnumerable<T> OfType<T>(this IEnumerable source)
{
    foreach (var item in source)
    {
        if (item is T typed)
            yield return typed;
    }
}
Метод ленивый: перебор происходит только при итерации по результату, не сразу при вызове. Когда использовать Удобно при работе с IEnumerable<object>, коллекциями ArrayList из старого кода, или когда нужно вытащить конкретный тип из гетерогенной иерархии объектов.
var textBoxes = Controls.OfType<TextBox>();
Чем отличается от Cast<T> Cast<T> бросает InvalidCastException, если встречает несовместимый тип. OfType<T> просто пропускает такой элемент. Если коллекция точно однородная и ошибка должна сигнализировать о проблеме, уместен Cast<T>. В остальных случаях безопаснее OfType<T>. 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека шарписта #sharp_view

🧑‍💻 Больше не нужно открывать терминал от администратора На Linux и macOS всё просто: нужны права администратора, добавили
🧑‍💻 Больше не нужно открывать терминал от администратора На Linux и macOS всё просто: нужны права администратора, добавили sudo перед командой. На Windows для этого приходилось открывать новый терминал от имени администратора, делать правый клик, подтверждать UAC. Microsoft наконец добавила sudo в Windows 11 нативно, начиная со сборки 26045. Что это такое Sudo for Windows позволяет запускать команды с правами администратора прямо из обычного терминала, без переоткрытия сессии. Это не порт Unix-утилиты, а отдельная Windows-реализация той же идеи. Некоторые детали поведения отличаются от привычного sudo на Linux. Как включить Через настройки: Параметры → Система → Для разработчиков → Включить sudo. Или через командную строку с правами администратора:
sudo config --enable normal
Три режима работы forceNewWindow — режим по умолчанию. Команда запускается в новом окне с правами администратора. Похоже на runas, но удобнее. disableInput — команда выполняется в текущем окне, но без возможности ввода. Подходит для автоматических задач, где интерактивный ввод не нужен. normal — полный инлайн-режим, как на Linux. Команда запускается в текущем окне и может принимать ввод. Наиболее удобный вариант, но несёт больше рисков безопасности. Переключение между режимами:
sudo config --enable forceNewWindow
sudo config --enable disableInput
sudo config --enable normal
Примеры использования Редактирование системного файла:
sudo notepad C:\Windows\System32\drivers\etc\hosts
Установка глобального npm-пакета:
sudo npm install -g package-name
Проверка сетевых соединений:
sudo netstat -ab
Про безопасность При запуске через sudo появляется UAC-запрос на подтверждение. Режимы disableInput и normal несут дополнительные риски: нежелательный процесс с низкими правами теоретически может взаимодействовать с повышенным процессом. Для большинства сценариев рекомендован forceNewWindow. Что нужно учесть Инструмент доступен только на Windows 11, начиная с версии 24H2. Управлять им можно через настройки, командную строку или реестр (HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Sudo). Есть и GPO-политика для корпоративного управления. ➡️ Репозиторий 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека шарписта #sharp_view

⚡️ Мы рады представить команду экспертов курса AgentOps! — Дмитрий Антипов расскажет, как грамотно проверить работу AI-моделе
⚡️ Мы рады представить команду экспертов курса AgentOps!Дмитрий Антипов расскажет, как грамотно проверить работу AI-моделей — Курилл Кухарев поделится, почему компаниям выгодно использовать локальные модели и как их развернуть — Андрей Носов расскажет, как работать с данными и знаниями в AI-системах: построение RAG, выбор подходов к поиску и организация хранения данных — Антон Будняк разберет, как обеспечить устойчивость сервиса, в котором используется ИИ — Александр Ошурков расскажет, как оценивать качество работы LLM в backend-сервисах — Екатерина Трофимов разберет, как проектировать инструменты для AI-агентов и выстраивать взаимодействие с внешними сервисами Курс для backend-разработчиков, тимлидов и LLM инженеров о том, как внедрять AI-логику в бэкенд IT-продуктов и сохранять стабильность сервиса. К концу обучения вы получите: • Структурированный подход к архитектуре и деплою AI-агентов • Навыки настройки мониторинга, тестирования и контроля расходов на токены • Разбор сложных инженерных кейсов из реальной практики 🎁 Доступ к материалам курса «Разработка ИИ-агентов» в подарок при покупке Инженерного трека 👉 Все подробности и программа обучения.

✏️ Паттерн репозиторий: неочевидные моменты Репозиторий скрывает логику работы с данными за интерфейсом. Контроллер или сервис не знает, откуда берётся объект: из базы, кеша или файла. Это удобно, но у паттерна есть нюансы, о которых редко говорят. Зачем он вообще нужен Без репозитория бизнес-логика тесно связана с DbContext. Тесты требуют реальной базы или громоздких моков. При смене ORM надо переписывать половину приложения. Репозиторий даёт одну точку входа для работы с конкретной сущностью и позволяет подменять реализацию без изменения вызывающего кода. Неочевидный момент 1: SaveChanges внутри репозитория Если вызывать SaveChangesAsync() в каждом методе репозитория, теряется возможность атомарно сохранить несколько операций. Например, создать пользователя и сразу выдать ему роль в одной транзакции не получится. Решение: убрать SaveChanges из репозитория и перенести его в Unit of Work или вызывать явно на уровне сервиса.
public interface IUnitOfWork : IDisposable
{
    IUserRepository Users { get; }
    IRoleRepository Roles { get; }
    Task<int> SaveChangesAsync();
}

public class UnitOfWork : IUnitOfWork
{
    private readonly AppDbContext _context;

    public IUserRepository Users { get; }
    public IRoleRepository Roles { get; }

    public UnitOfWork(AppDbContext context)
    {
        _context = context;
        Users = new UserRepository(context);
        Roles = new RoleRepository(context);
    }

    public async Task<int> SaveChangesAsync()
        => await _context.SaveChangesAsync();

    public void Dispose() => _context.Dispose();
}
Неочевидный момент 2: Generic-репозиторий не всегда помогает Соблазн велик написать один GenericRepository<T> и использовать везде:
public class GenericRepository<T> : IRepository<T> where T : class
{
    protected readonly AppDbContext _context;
    protected readonly DbSet<T> _dbSet;

    public GenericRepository(AppDbContext context)
    {
        _context = context;
        _dbSet = context.Set<T>();
    }

    public async Task<T?> GetByIdAsync(int id) => await _dbSet.FindAsync(id);
    public async Task<IEnumerable<T>> GetAllAsync() => await _dbSet.ToListAsync();
    public async Task AddAsync(T entity) => await _dbSet.AddAsync(entity);
    public void Update(T entity) => _dbSet.Update(entity);
    public void Delete(T entity) => _dbSet.Remove(entity);
}
Проблема в том, что специфичные запросы всё равно придётся добавлять. Найти пользователя по почте через универсальный метод не выйдет без дополнительных расширений. В итоге generic-репозиторий обрастает методами и теряет смысл. Лучше наследовать от него там, где действительно нет специфики, а сложные сущности описывать отдельными интерфейсами:
public interface IUserRepository : IRepository<User>
{
    Task<User?> GetByEmailAsync(string email);
    Task<IEnumerable<User>> GetActiveUsersAsync();
}

public class UserRepository : GenericRepository<User>, IUserRepository
{
    public UserRepository(AppDbContext context) : base(context) { }

    public async Task<User?> GetByEmailAsync(string email)
        => await _dbSet.FirstOrDefaultAsync(u => u.Email == email);

    public async Task<IEnumerable<User>> GetActiveUsersAsync()
        => await _dbSet.Where(u => u.IsActive).ToListAsync();
}
Неочевидный момент 3: EF Core уже репозиторий DbContext и DbSet<T> это и есть реализация паттерна репозиторий. DbSet — коллекция объектов, DbContext — Unit of Work. Оборачивать EF Core в ещё один слой репозитория иногда только добавляет лишний код. Это оправдано, когда нужно: - тестировать бизнес-логику без базы (мок интерфейса проще, чем мок DbContext) - поддерживать несколько источников данных - изолировать слои приложения архитектурно Если проект небольшой и планов менять ORM нет, прямое использование DbContext в сервисах может быть вполне нормальным решением. 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека шарписта #il_люминатор

🤩 Фоновые задачи без лишних зависимостей Если вы запускаете фоновые задачи через Task.Run или городите велосипед поверх IHostedService, знакомьтесь с BusyBee. Это небольшая библиотека для in-memory обработки фоновых задач в .NET, которая построена на нативных каналах (System.Threading.Channels). Какую проблему решает Хочется просто поставить задачу в очередь и не думать об управлении потоками, таймаутах и наблюдаемости. Стандартные средства .NET это умеют, но требуют немало бойлерплейта. Hangfire или Quartz — тяжеловато, если нужно только in-memory без персистентности. Что умеет BusyBee Ограниченные и неограниченные очереди с настройкой поведения при переполнении: можно выбросить исключение, подождать, отбросить самую старую или новую задачу. Параллельное выполнение задач с настраиваемым пулом слотов. Глобальные и per-job таймауты. Интеграция с OpenTelemetry для трассировки и метрик. Полная поддержка DI и CancellationToken. Как подключить:
dotnet add package BusyBee
Регистрация в DI:
builder.Services
    .AddBusyBee()
    .WithUnboundedQueue()
    .WithGlobalJobTimeout(TimeSpan.FromSeconds(30))
    .WithLevelOfParallelism(10);
Поставить задачу в очередь:
await queue.Enqueue(async (services, context, cancellationToken) =>
{
    var logger = services.GetRequiredService<ILogger<Program>>();
    logger.LogInformation("Обрабатываем задачу {JobId}", context.JobId);
    await Task.Delay(1000, cancellationToken);
}, cancellationToken);
Каждая задача получает контекст с JobId, временем постановки в очередь и временем старта. Через services доступны любые зарегистрированные сервисы. Ограниченная очередь:
builder.Services
    .AddBusyBee()
    .WithBoundedQueue(capacity: 1000, OverflowStrategy.DropOldest);
Подключение OpenTelemetry:
builder.Services.AddOpenTelemetry()
    .WithTracing(tracing => tracing
        .AddSource(BusyBee.Observability.TracingConstants.TraceSourceName)
        .AddConsoleExporter())
    .WithMetrics(metrics => metrics
        .AddMeter(BusyBee.Observability.MetricsConstants.MeterName)
        .AddPrometheusExporter());
Обработка ошибок Можно подключить собственные обработчики для сбоев, таймаутов и ручной отмены задач через реализацию интерфейсов IJobFailureHandler, IJobTimeoutHandler и IManualCancellationHandler:
services.AddBusyBee()
    .WithJobFailureHandler<MyCustomJobFailureHandler>();
BusyBee подойдёт, когда нужен простой in-memory обработчик в фоне без внешних зависимостей и баз данных. ➡️ Репозиторий 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека шарписта #sharp_view

🏃‍♀️ Уже завтра стартует курс по разработке AI-агентов. Про AI-агентов часто думают, что это просто модная обертка над джпт
🏃‍♀️ Уже завтра стартует курс по разработке AI-агентов. Про AI-агентов часто думают, что это просто модная обертка над джпт для пет-проектов. Кажется, прикрутил API к скрипту и типа готово. А вот и нет! Когда дело доходит до прода, начинаются настоящие проблемы.
Зачем глубоко копать мультиагентные системы, если можно обойтись старым добрым кодом? Как контролировать расходы на токены, чтобы новая фича не разорила бизнес? Как заставить агента работать стабильно и предсказуемо, а не галлюцинировать?
Эту инженерную часть мы и будем разбирать на курсе. Будем учиться интегрировать внешние API, работать с RAG, LangGraph, CrewAI и деплоить всё это так, чтобы работало как часы. Стартуем завтра. Для участия и доступа к программе переходите по ссылке.

🔍 ILSpy 10.0: поддержка C# 14, API diff и переход на .NET 10 Вышел крупный релиз ILSpy. Релиз собран на .NET 10, так что пер
🔍 ILSpy 10.0: поддержка C# 14, API diff и переход на .NET 10 Вышел крупный релиз ILSpy. Релиз собран на .NET 10, так что перед обновлением убедитесь, что он у вас установлен. Новые языковые возможности Декомпилятор теперь понимает конструкции C# 13 и C# 14. Добавлена поддержка ограничения allows ref struct из C# 13 и extension-членов из C# 14. Также подтянули поддержку InlineArray из C# 12 — на удивление поздно, но лучше поздно чем никогда. Детекция паттернов обновлена до Roslyn 5.0 RTM. Новое в интерфейсе Появился выделенный UI для работы с C# 14 extension-членами. Добавлена навигация по видимой истории — теперь можно перемещаться по посещённым узлам в дереве сборок. Из поставки убрали переводы интерфейса — проект сосредоточился на английском. API diff Одна из самых интересных новинок. ILSpy теперь умеет сравнивать публичное API двух сборок и показывать разницу. Полезно при анализе изменений между версиями библиотек, когда исходников нет. Улучшения декомпилятора Исправлена корректная декомпиляция pre-increment операторов, unmanaged function pointers и локальных функций с параметрами по умолчанию. Улучшено определение record-типов и primary constructors. CSharpConversions получил общие улучшения для правил C# вплоть до версии 9. Дизассемблер теперь поддерживает формат ildasm /caverbal. Пакетная генерация PDB Новый метод GeneratePdbForAssemblies заменил старый GeneratePdbForAssembly — теперь можно генерировать PDB для нескольких сборок за один вызов. Чистка API Удалены устаревшие типы: UnresolvedUsingScope, ToTypeReference, ITypeReference. ResolvedUsingScope переименован в UsingScope. Если вы используете ICSharpCode.Decompiler как NuGet-пакет в своих инструментах — стоит проверить совместимость. Производительность Убрана XML-сериализация из DecompilerSettings — настройки теперь сохраняются быстрее. Важное предупреждение Команда отдельно предупреждает: домен ilspy.org им не принадлежит. Скачивать ILSpy нужно только с GitHub Releases. ➡️ Источник 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека шарписта #async_news

🦾 Надоело чинить «упавших» ИИ-агентов после каждого микросбоя внешних сервисов? Анонсируем старт продаж большого курса по Ag
🦾 Надоело чинить «упавших» ИИ-агентов после каждого микросбоя внешних сервисов? Анонсируем старт продаж большого курса по AgentOps. Мы собрали опыт десятков разработчиков и сделали программу, которая учит выводить ИИ в стабильный прод. 🗓 Ждем вас 28 апреля в 19:00 МСК на эфире: «Как эффективно управлять контекстным окном LLM в мультиагентных системах и не сливать бюджет на токены». 👉 Кто вещает и в чем польза? Спикер Кирилл Кухарев (Senior AI Engineer в Raft, спикер AI Conf и Highload++). Он реализовал более 50 коммерческих проектов в GenAI и на вебинаре покажет, как взять под контроль работу нескольких агентов, чтобы они не перекидывали друг другу лишний контекст и не сжигали ваши деньги. В прямом эфире разберем: • Как формируется контекст в LLM при маршрутизации между агентами; • Куда утекают лишние токены и возникает перерасход; • Практические методы: как сжимать историю, грамотно делить задачи, лимитировать передачу контекста и собирать промпты прямо в процессе запроса пользователя. 🔥 Два способа получить максимум: 1. Приходите на вебинар 28 апреля. Дарим участникам промокод на 5.000 ₽ (работает 3 дня после эфира - это шанс забрать курс по самому низу рынка). 2. Выбирайте Инженерный трек. В подарок к нему идет полный доступ к записям и автопроверкам завершенного курса «Разработка ИИ-агентов». 👉 Занять место на вебинаре и стать профи в AgentOps

👨‍💻 Не показывайте технические ошибки пользователям Когда приложение падает с необработанным исключением, стандартное поведение многих фреймворков — вывести стек вызовов прямо в ответ. Пользователь видит внутренние пути файлов, названия классов, фрагменты кода. Для злоумышленника это готовая карта уязвимостей. Что происходит на практике Допустим, в ASP.NET приложении возникает необработанное исключение. Без явной настройки middleware фреймворк вернёт подробный ExceptionDetails с именем метода, строкой кода и трассировкой стека. Атакующий получает информацию о структуре проекта, версиях библиотек и логике работы приложения без каких-либо усилий. Как это закрыть Правильный подход это перехват всех необработанныъ исключений. Нужно логировать их внутри системы и возвращать пользователю только нейтральное сообщение. В ASP.NET это делается через UseExceptionHandler:
app.UseExceptionHandler(errorApp =>
{
    errorApp.Run(async context =>
    {
        var exceptionHandlerPathFeature =
            context.Features.Get<IExceptionHandlerPathFeature>();

        var logger = context.RequestServices
            .GetRequiredService<ILogger<Program>>();

        logger.LogError(exceptionHandlerPathFeature?.Error,
            "Unhandled exception");

        context.Response.StatusCode = 500;

        await context.Response.WriteAsJsonAsync(new
        {
            message = "Something went wrong. Please try again later."
        });
    });
});
Что здесь происходит: middleware перехватывает исключение, передаёт его в ILogger — туда, где его увидит только команда разработки — и возвращает клиенту простой JSON с универсальным сообщением об ошибке. Никаких деталей реализации наружу не уходит. ILogger пишет в вашу систему мониторинга — будь то Application Insights, Seq, Serilog или любой другой инструмент. Вы по-прежнему видите полный стек вызовов и можете разобраться в причине ошибки. Пользователь при этом получает понятное сообщение, а не технический мусор. Это базовая практика защиты приложений. Она не требует сложной архитектуры, достаточно одного middleware, настроенного в точке входа приложения. 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека шарписта #sharp_view

👀 Смотрим внутрь любой .NET-сборки Бывает, что нужно понять, что делает библиотека без исходников, разобраться в чужом коде
👀 Смотрим внутрь любой .NET-сборки Бывает, что нужно понять, что делает библиотека без исходников, разобраться в чужом коде или проверить, что именно скомпилировал компилятор. ILSpy решает эту задачу это открытый декомпилятор .NET-сборок, который превращает IL-байткод обратно в читаемый C#. ILSpy открывает любой .dll или .exe и показывает декомпилированный C#-код с навигацией по типам, методам и свойствам. Поддерживает поиск по всей сборке, переходы по гиперссылкам между типами и историю навигации. Visual Studio 2022 и 2026 используют движок ILSpy внутри для F12-навигации по декомпилированным источникам, так что если вы нажимали «Go to Definition» на тип из NuGet-пакета, вы уже работали с ILSpy. CLI-инструмент для Linux, Mac и Windows:
dotnet tool install -g ilspycmd
После установки можно декомпилировать прямо из терминала:
ilspycmd MyLibrary.dll
Использование как библиотеки Движок декомпилятора доступен отдельным NuGet-пакетом ICSharpCode.Decompiler. Его можно встроить в собственные инструменты:
var decompiler = new CSharpDecompiler("MyLibrary.dll", new DecompilerSettings());
var code = decompiler.DecompileWholeModuleAsString();
Console.WriteLine(code);
➡️ Репозиторий 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека шарписта #sharp_view

❓ Что выведет код с картинки 💡 Подсказка: обратите внимание на конструкцию when. Это не просто синтаксический сахар. ➡️ А от
Что выведет код с картинки 💡 Подсказка: обратите внимание на конструкцию when. Это не просто синтаксический сахар. ➡️ А ответ тут 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека шарписта #dotnet_challenge

👨‍💻 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_люминатор

🔒 Три строки, которые закрывают базовые уязвимости Браузеры умеют защищать пользователей от ряда атак, но только если сервер явно это разрешает через 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_люминатор