Библиотека шарписта | C#, F#, .NET, ASP.NET
Все самое полезное для C#-разработчика в одном канале. По рекламе: @proglib_adv Учиться у нас: https://proglib.io/w/b60af5a4 Для обратной связи: @proglibrary_feeedback_bot РКН: https://gosuslugi.ru/snet/67a5c81cdc130259d5b7fead
Показати більше📈 Аналітичний огляд Telegram-каналу Библиотека шарписта | C#, F#, .NET, ASP.NET
Канал Библиотека шарписта | C#, F#, .NET, ASP.NET (@csharpproglib) у мовному сегменті Російська є активним учасником. На даний момент спільнота об'єднує 21 866 підписників, посідаючи 6 212 місце в категорії Технології та додатки та 30 851 місце у регіоні Росія.
📊 Показники аудиторії та динаміка
З моменту свого створення невідомо, проект продемонстрував стрімке зростання, зібравши аудиторію у 21 866 підписників.
За останніми даними від 10 червня, 2026, канал демонструє стабільну активність. Хоча за останні 30 днів спостерігається зміна кількості учасників на -87, а за останні 24 години на -4, загальне охоплення залишається високим.
- Статус верифікації: Не верифікований
- Рівень залученості (ER): Середній показник залученості аудиторії становить 12.06%. Протягом перших 24 годин після публікації контент зазвичай збирає 7.04% реакцій від загальної кількості підписників.
- Охоплення публікацій: В середньому кожен допис отримує 2 638 переглядів. Протягом першої доби публікація в середньому набирає 1 540 переглядів.
- Реакції та взаємодія: Аудиторія активно підтримує контент: середня кількість реакцій на один пост – 8.
- Тематичні інтереси: Контент зосереджений навколо ключових тем, таких як .net, шарписта, навигация, await, string.
📝 Опис та контентна політика
Автор описує ресурс як майданчик для висловлення суб'єктивної думки:
“Все самое полезное для C#-разработчика в одном канале.
По рекламе: @proglib_adv
Учиться у нас: https://proglib.io/w/b60af5a4
Для обратной связи: @proglibrary_feeedback_bot
РКН: https://gosuslugi.ru/snet/67a5c81cdc130259d5b7fead”
Завдяки високій частоті оновлень (останні дані отримано 11 червня, 2026), канал підтримує актуальність та високий рівень охоплення публікацій. Аналітика показує, що аудиторія активно взаємодіє з контентом, що робить його важливою точкою впливу в категорії Технології та додатки.
node-gyp и конкретной версии Python на каждой машине разработчика. Для .NET-команды это лишняя зависимость, которую нужно поддерживать и в CI, и при онбординге новых участников.
Решение
Native AOT умеет компилировать .NET-код в нативную shared library с произвольными точками входа. Node.js аддоны требуют только одного: экспортированной функции napi_register_module_v1. Native AOT с этим справляется.
Как это работает
Аддон взаимодействует с Node.js через [N-API](https://nodejs.org/api/n-api.html) — стабильный C-совместимый интерфейс. Язык реализации значения не имеет, важно лишь соответствие сигнатурам.
Минимальный .csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<PublishAot>true</PublishAot>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
</Project>
PublishAot говорит SDK собрать нативную библиотеку при публикации. AllowUnsafeBlocks нужен из-за работы с указателями при интеропе с N-API.
Точка входа модуля:
[UnmanagedCallersOnly(
EntryPoint = "napi_register_module_v1",
CallConvs = [typeof(CallConvCdecl)])]
public static nint Init(nint env, nint exports)
{
Initialize();
RegisterFunction(env, exports, "readStringValue"u8, &ReadStringValue);
return exports;
}
Атрибут [UnmanagedCallersOnly] экспортирует метод с нужным именем и calling convention. Суффикс u8 создаёт ReadOnlySpan<byte> с UTF-8 строкой без аллокаций.
Вызов N-API из .NET
N-API функции экспортирует сам node.exe. Чтобы не линковаться с отдельной библиотекой, используется кастомный резолвер через NativeLibrary.SetDllImportResolver:
NativeLibrary.SetDllImportResolver(
Assembly.GetExecutingAssembly(),
(libraryName, assembly, searchPath) =>
libraryName is "node"
? NativeLibrary.GetMainProgramHandle()
: 0);
После этого P/Invoke с именем "node" будет резолвиться в хост-процесс:
[LibraryImport("node", EntryPoint = "napi_create_string_utf8")]
internal static partial Status CreateStringUtf8(
nint env, ReadOnlySpan<byte> str, nuint length, out nint result);
Пример экспортируемой функции
Функция читает значение из реестра Windows и возвращает его в JavaScript:
[UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
private static nint ReadStringValue(nint env, nint info)
{
try
{
var keyPath = GetStringArg(env, info, 0);
var valueName = GetStringArg(env, info, 1);
if (keyPath is null || valueName is null)
{
ThrowError(env, "Expected two string arguments: keyPath, valueName");
return 0;
}
using var key = Registry.CurrentUser.OpenSubKey(keyPath, writable: false);
return key?.GetValue(valueName) is string value
? CreateString(env, value)
: GetUndefined(env);
}
catch (Exception ex)
{
ThrowError(env, $"Registry read failed: {ex.Message}");
return 0;
}
}
Исключения нужно обязательно перехватывать: необработанное исключение в методе с [UnmanagedCallersOnly] крашит хост-процесс. ThrowError пробрасывает ошибку в JavaScript как стандартный Error.
Что в итоге
Команда убрала зависимость от Python и node-gyp. Теперь yarn install работает только с Node.js и .NET SDK, которые и так нужны для разработки. CI стал проще, онбординг быстрее.
Производительность сопоставима с C++-реализацией: Native AOT генерирует оптимизированный нативный код, а для задач типа доступа к реестру и маршалинга строк разницы на практике нет.
➡️ Источник
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#sharp_viewMicrosoft.AspNetCore.DataProtection, который попал в релиз вместе с плановым обновлением 10.0.6.
Что случилось
После выхода 10.0.6 пользователи начали сообщать, что расшифровка данных перестала работать. Microsoft изучила проблему и обнаружила, что регрессия также открывает уязвимость CVE-2026-40372.
Суть бага: управляемый энкриптор вычислял HMAC-тег не над теми байтами полезной нагрузки, а потом и вовсе выбрасывал результат. Это приводило к повышению привилегий. Затронуты версии Microsoft.AspNetCore.DataProtection от 10.0.0 до 10.0.6 включительно.
Кого это касается
Всех, кто использует ASP.NET Core Data Protection в приложениях на .NET 10. Это стандартный механизм защиты данных в ASP.NET Core — сессии, антифоргери-токены, куки и всё, что шифруется через IDataProtector.
Что делать
Обновить пакет Microsoft.AspNetCore.DataProtection до версии 10.0.7 как можно скорее:
dotnet add package Microsoft.AspNetCore.DataProtection --version 10.0.7
После обновления пакета пересобрать и перевыложить приложение. Проверить текущую версию SDK можно командой:
dotnet --info
➡️ Источник
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#async_newsКак эффективно управлять контекстным окном LLM в мультиагентных системах и не сливать бюджет на токеныВ кружке Кирилл рассказал, какие именно подходы будем разбирать. 👉 Занять место на вебинаре
<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_viewsudo перед командой. На 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_viewDbContext. Тесты требуют реальной базы или громоздких моков. При смене 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Зачем глубоко копать мультиагентные системы, если можно обойтись старым добрым кодом? Как контролировать расходы на токены, чтобы новая фича не разорила бизнес? Как заставить агента работать стабильно и предсказуемо, а не галлюцинировать?Эту инженерную часть мы и будем разбирать на курсе. Будем учиться интегрировать внешние API, работать с RAG, LangGraph, CrewAI и деплоить всё это так, чтобы работало как часы. Стартуем завтра. Для участия и доступа к программе переходите по ссылке.
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_newsExceptionDetails с именем метода, строкой кода и трассировкой стека. Атакующий получает информацию о структуре проекта, версиях библиотек и логике работы приложения без каких-либо усилий.
Как это закрыть
Правильный подход это перехват всех необработанныъ исключений. Нужно логировать их внутри системы и возвращать пользователю только нейтральное сообщение.
В 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.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
Вже доступно! Дослідження Telegram за 2025 — головні інсайти року 
