Библиотека шарписта | C#, F#, .NET, ASP.NET
Все самое полезное для C#-разработчика в одном канале. Наши курсы: https://clc.to/y3LDtw По рекламе: @tproger_sales_bot Для обратной связи: @proglibrary_feeedback_bot РКН: https://gosuslugi.ru/snet/67a5c81cdc130259d5b7fead
Show more📈 Analytical overview of Telegram channel Библиотека шарписта | C#, F#, .NET, ASP.NET
Channel Библиотека шарписта | C#, F#, .NET, ASP.NET (@csharpproglib) in the Russian language segment is an active participant. Currently, the community unites 21 629 subscribers, ranking 5 973 in the Technologies & Applications category and 30 111 in the Russia region.
📊 Audience metrics and dynamics
Since its creation on невідомо, the project has demonstrated rapid growth, gathering an audience of 21 629 subscribers.
According to the latest data from 05 October, 2026, the channel demonstrates stable activity. Although there has been a change in the number of participants by -56 over the last 30 days and by 3 over the last 24 hours, overall reach remains high.
- Verification status: Not verified
- Engagement rate (ER): The average audience engagement rate is 15.56%. Within the first 24 hours after publication, content typically collects 7.66% reactions from the total number of subscribers.
- Post reach: On average, each post receives 3 367 views. Within the first day, a publication typically gains 1 657 views.
- Reactions and interaction: The audience actively supports content: the average number of reactions per post is 17.
- Thematic interests: Content is focused on key topics such as .net, шарписта, навигация, await, string.
📝 Description and content policy
The author describes the resource as a platform for expressing subjective opinions:
“Все самое полезное для C#-разработчика в одном канале.
Наши курсы: https://clc.to/y3LDtw
По рекламе: @tproger_sales_bot
Для обратной связи: @proglibrary_feeedback_bot
РКН: https://gosuslugi.ru/snet/67a5c81cdc130259d5b7fead”
Thanks to the high frequency of updates (latest data received on 06 October, 2026), the channel maintains relevance and a high level of publication reach. Analytics show that the audience actively interacts with content, making it an important point of influence in the Technologies & Applications category.
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