Библиотека шарписта | C#, F#, .NET, ASP.NET
Все самое полезное для C#-разработчика в одном канале. Наши курсы: https://clc.to/y3LDtw По рекламе: @tproger_sales_bot Для обратной связи: @proglibrary_feeedback_bot РКН: https://gosuslugi.ru/snet/67a5c81cdc130259d5b7fead
显示更多📈 Telegram 频道 Библиотека шарписта | C#, F#, .NET, ASP.NET 的分析概览
频道 Библиотека шарписта | C#, F#, .NET, ASP.NET (@csharpproglib) 俄语 语言赛道中的 是活跃参与者。目前社区聚集了 21 629 名订阅者,在 技术与应用 类别中位列第 5 973,并在 俄罗斯 地区排名第 30 111 位。
📊 受众指标与增长动态
自 невідомо 创建以来,项目保持高速增长,吸引了 21 629 名订阅者。
根据 05 十月, 2026 的最新数据,频道保持稳定运转。过去 30 天订阅人数变化为 -56,过去 24 小时变化为 3,整体触达仍然可观。
- 认证状态: 未认证
- 互动率 (ER): 平均受众互动率为 15.56%。内容发布后 24 小时内通常能获得 7.66% 的反应,占订阅者总量。
- 帖子覆盖: 每篇帖子平均可获得 3 367 次浏览,首日通常累积 1 657 次浏览。
- 互动与反馈: 受众积极参与,单帖平均反应数为 17。
- 主题关注点: 内容集中在 .net, шарписта, навигация, await, string 等核心主题上。
📝 描述与内容策略
作者将该频道定位为表达主观观点的平台:
“Все самое полезное для C#-разработчика в одном канале.
Наши курсы: https://clc.to/y3LDtw
По рекламе: @tproger_sales_bot
Для обратной связи: @proglibrary_feeedback_bot
РКН: https://gosuslugi.ru/snet/67a5c81cdc130259d5b7fead”
凭借高频更新(最新数据采集于 06 十月, 2026),频道始终保持新鲜度与高覆盖。分析显示受众积极互动,使其成为 技术与应用 类别中的关键影响点。
logger.LogInformation($"User {userId} logged in at {time}");
⏬ Компилятор разворачивает это примерно в:
logger.LogInformation(string.Format("User {0} logged in at {1}", userId, time));
Строка формируется до вызова метода. Если уровень логирования Information отключён, строка всё равно создаётся, занимает память и тут же выбрасывается сборщиком мусора.
⏬ Структурное логирование решает проблему:
logger.LogInformation("User {UserId} logged in at {Time}", userId, time);
Здесь строка — это шаблон. Аргументы передаются отдельно. Логгер сначала проверяет, активен ли уровень, и только потом форматирует сообщение. Если уровень выключен — аллокации нет вообще.
Бонус: структурное логирование позволяет индексировать поля в системах вроде Seq, Elasticsearch, Datadog — вы сможете искать по UserId как по полю, а не парсить текст.
⏬ Начиная с .NET 6 есть ещё лучший вариант через compile-time source generators:
// Генерирует оптимальный код на этапе компиляции:
[LoggerMessage(Level = LogLevel.Information, Message = "User {UserId} logged in at {Time}")]
partial void LogUserLogin(int userId, DateTime time);
Никаких аллокаций, никакого боксинга, максимальная производительность — и всё это без изменения читаемости кода.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#il_люминатор
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
dotnet nuget trust author Microsoft \
9A1B131BEE0605433056A4EA3815478A8E177961A968C6C0027C1093D1FEB630 \
--algorithm SHA256
Старые сертификаты удалять не нужно — они нужны для уже опубликованных пакетов.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#async_news
var task = Task.Run(() => { });
if (!task.Wait(3000))
{
// ThreadPool, вероятно, перегружен
// → создаём memory dump
}
➡️ Dump позволяет посмотреть:
— какие потоки зависли;
— где стоят Task;
— что происходит в ThreadPool;
— какие объекты находятся в памяти.
На Windows для создания dump можно использовать MiniDumpWriteDump, а в Linux — встроенный в .NET createdump. А дальше .dmp открывается в Visual Studio — можно исследовать call stacks, переменные и состояние потоков.
💡 Особенно полезно для production-проблем, которые невозможно нормально воспроизвести локально.
🔗 Источник
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#sharp_view
IEnumerable<int> GetIds()
{
foreach (var item in _items)
yield return item.Id;
}
Читается как обычный цикл. Но компилятор превращает такой метод в конечный автомат со вспомогательными классами и состоянием. Именно поэтому итераторы такие выразительные — но и именно поэтому они не бесплатны.
❓ Где это становится проблемой
На большинстве путей yield return это правильный выбор. Код чище, API удобнее, читаемость выше.
Но на горячих путях, где метод вызывается тысячи раз в секунду, повторное создание объектов итератора и дополнительные слои абстракции начинают влиять на производительность. Не катастрофически, но измеримо.
❓ Что делать на критичных участках
Если профилировщик показал, что итератор узкое место, есть несколько альтернатив.
🟡 Заполнение буфера, переданного вызывающей стороной:
void FillIds(Span<int> buffer)
{
for (int i = 0; i < _items.Count; i++)
buffer[i] = _items[i].Id;
}
🟡 Возврат конкретной коллекции:
List<int> GetIds()
{
var result = new List<int>(_items.Count);
foreach (var item in _items)
result.Add(item.Id);
return result;
}
🟡 Прямой цикл внутри горячего кода без промежуточных перечислений:
for (int i = 0; i < _items.Count; i++)
Process(_items[i].Id);
yield return стоит использовать тогда, когда он делает API лучше и код понятнее. Но не стоит считать его нулевым по стоимости. Если участок горячий, то сначала замерьте, потом решайте.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#il_люминаторvar result = text.Replace("cat", "dog");
🔜 Regex.Replace() — когда нужен шаблон:
var result = Regex.Replace(
text,
@"\d+",
"#");
Например, заменить все числа на #.
🔜 StringBuilder.Replace() — когда последовательно изменяем большой текст:
var sb = new StringBuilder(text);
sb.Replace("cat", "dog");
sb.Replace("foo", "bar");
var result = sb.ToString();
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#sharp_view
private int _counter;
public void Increment()
{
Interlocked.Increment(ref _counter);
}
Без Interlocked операция _counter++ на самом деле три шага: прочитать, прибавить, записать. Два потока могут выполнить их одновременно и затереть результат друг друга. Interlocked.Increment делает всё это за один неделимый шаг.
➡️ Lock-free генератор ID
Interlocked подходит для генерации уникальных идентификаторов без блокировок:
public int GetNextId()
{
return Interlocked.Increment(ref _id);
}
Каждый вызов гарантированно вернёт уникальное значение, даже если тысячи потоков вызывают метод одновременно.
➡️ CompareExchange: условное обновление
Самая мощная операция в арсенале Interlocked:
if (Interlocked.CompareExchange(ref _state, 1, 0) == 0)
{
// Успешно перешли из состояния 0 в 1
}
Логика простая: если текущее значение равно 0, заменить на 1 и вернуть старое значение. Если кто-то уже изменил _state, операция ничего не сделает. Это паттерн, на котором строятся lock-free кэши, планировщики и конкурентные очереди.
➡️ Volatile и Interlocked — не одно и то же
Их можно перепутать, но они решают разные задачи.
Volatile гарантирует, что поток видит актуальное значение переменной. Interlocked гарантирует, что сама операция выполняется безопасно, без вмешательства других потоков.
В реальных системах нередко нужны оба. Volatile чтобы читать свежие данные, Interlocked чтобы безопасно их менять.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#sharp_view
app.Use(async (context, next) =>
{
Console.WriteLine("Request");
await next(context);
Console.WriteLine("Response");
});
1️⃣ Забыл next() — остановил pipeline
await _next(context);
Если его не вызвать, middleware ниже и endpoint не выполнятся.
2️⃣ Scoped-сервис не стоит инжектить в конструктор
Middleware переиспользуется между запросами, поэтому Scoped лучше получать в InvokeAsync:
public async Task InvokeAsync(
HttpContext context,
IMyScopedService service)
{
await _next(context);
}
3️⃣ После начала ответа менять его уже нельзя
context.Response.StatusCode = 503; // 💥
Если response уже отправляется, будет InvalidOperationException.
💡 Для действий до отправки используйте Response.OnStarting().
И ещё: не передавайте scoped-сервис в Task.Run после завершения запроса — его scope уже мог быть уничтожен. Для фоновой работы создавайте новый scope или используйте BackgroundService 🧠
🔗 Источник
📍 Навигация: Вакансии • Задачи • Собесы
🐸Библиотека шарписта
#il_люминатор
function foo(a: { Do(): void }) {
a.Do();
}
🔜 В C# так напрямую нельзя:
class A
{
public void Do() => Console.WriteLine("A.Do");
}
class B
{
public void Do() => Console.WriteLine("B.Do");
}
🔜 У A и B нет общего интерфейса. Но хочется вызвать:
Foo(new A()); Foo(new B());И здесь на сцену выходят interceptors — экспериментальная возможность компилятора C#. 🌞 Идея:
[DuckShape]
public interface IDoable
{
void Do();
}
public static partial class Ops
{
[DuckTyped]
public static void Foo(IDoable value)
=> value.Do();
}
📍 Source generator создаёт адаптер:
internal readonly struct ShapeAdapter_IDoable_A : IDoable
{
private readonly A _value;
public ShapeAdapter_IDoable_A(A value)
=> _value = value;
public void Do() => _value.Do();
}
А interceptor подменяет вызов и оборачивает A или B в подходящий адаптер.
🔴 В итоге:
Ops.Foo(new A()); // A.Do Ops.Foo(new B()); // B.DoРаботает и с properties — например, можно описать INameable с Name { get; set; }, а затем передать обычный Person, не меняя его код. ⚠️ Но это скорее интересный PoC, чем готовая замена интерфейсам: — interceptors остаются экспериментальной возможностью; — нужен source generator; — есть ограничения и не покрытые edge cases; — появляется дополнительная магия на этапе компиляции. 🔗 Источник 📍 Навигация: Вакансии • Задачи • Собесы 🐸Библиотека шарписта #sharp_view
— managed heap; — JIT и загруженные сборки; — стеки потоков; — native-библиотеки; — буферы сокетов; — runtime и TLS.Поэтому одного GCHeapHardLimitPercent недостаточно: он ограничивает только managed heap. 🔴 Что проверить:
kubectl top pod
dotnet-counters monitor -p <pid> System.Runtime
dotnet-gcdump collect -p <pid>
dotnet-dump collect -p <pid>
🔴 И ещё несколько практических моментов:
→ не выставляй всем подам одинаковые memory limits;
→ проверь AsNoTracking() и проекции в EF Core;
→ ищи .Result, .Wait() и блокирующий I/O;
→ тестируй GC под реальным memory limit контейнера.
🔗 Источник
📍 Навигация: Вакансии • Задачи • Собесы
🐸Библиотека шарписта
#il_люминатор
var task = GetDataAsync();
// забыли await
Use(task);
Это режим «запустил и забыл» (fire-and-forget). Последствия:
• выполнение продолжается немедленно
• зависимость не обеспечивается
• исключения выходят за пределы логического стека вызовов
Что может произойти с приложением
⚠️ Забытый await может привести к классическому состоянию гонки: код продолжает выполнение до завершения критической операции, например, проверки прав доступа, открывая окно для эксплуатации.
Атакующий может успеть получить доступ к данным между моментом начала проверки и её завершением.
⚠️ Когда исключения выходят за пределы логического стека вызовов, они могут быть проглочены без логирования. Это маскирует неудачные попытки аутентификации, SQL-инъекции или другие атаки.
⚠️ Истощение пула потоков как вектор DoS-атаки. При высокой нагрузке неправильное использование async/await приводит к истощению пула потоков. Злоумышленник может намеренно генерировать запросы, эксплуатирующие блокирующие операции.
📍 Навигация: Вакансии • Задачи • Собесы
🐸Библиотека шарписта
#il_люминатор.NET Core на .NET 11
В C# 15 можно описать тип с несколькими допустимыми вариантами:
public union IntOrString(int, string);
Такой endpoint может вернуть либо число, либо строку:
app.MapGet("/max-unavailable", () => new IntOrString(2));
JSON останется обычным:
2 или: "25%"Без $type и дополнительных обёрток. Главный плюс — компилятор проверяет, что switch обработал все варианты union. Поддерживается в Minimal APIs, MVC, SignalR, Blazor и OpenAPI. 📍 Навигация: Вакансии • Задачи • Собесы 🐸Библиотека шарписта #async_news
RuleFor(x => x.ShippingAddress)
.NotEmpty()
.When(x => x.DeliveryMethod == "Express");
RuleFor(x => x.CreditCard)
.NotEmpty()
.Unless(x => x.PaymentMethod == "PayPal");
When проверяет условие перед правилом. Express доставка требует адрес, PayPal не требует карту. Логика в одном месте без if-else в контроллере.
📍 Навигация: Вакансии • Задачи • Собесы
🐸Библиотека шарписта
#sharp_view