Библиотека шарписта | 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),频道始终保持新鲜度与高覆盖。分析显示受众积极互动,使其成为 技术与应用 类别中的关键影响点。
Where и Select сами по себе ленивые, они не выполняются пока не началось перечисление. Но как только появляется ToArray, начинается реальная работа и выделяется новый массив под результат
Если внутри цикла постоянно вызывается ToArray, то постоянно создаются новые массивы.
Если еще и лямбды захватывают переменные, добавляются лишние замыкания, и это тоже может стать частью мусора, который потом будет собирать GC.
Пример:
foreach (var batch in batches)
{
var validItems = batch
.Where(IsValid)
.Select(Transform)
.ToArray();
Process(validItems);
}
Ручной цикл выглядит скучнее, но дает контроль над буфером и позволяет переиспользовать память:
var buffer = new List<Result>(batch.Count);
foreach (var item in batch)
{
if (!IsValid(item))
continue;
buffer.Add(Transform(item));
}
Process(CollectionsMarshal.AsSpan(buffer));
buffer.Clear();
CollectionsMarshal.AsSpan дает доступ к внутреннему массиву List как Span, то есть можно передать данные дальше без копирования, но важно не менять List пока Span используется.
📍 Навигация: Вакансии • Задачи • Собесы
🐸Библиотека шарписта
#sharp_view Microsoft, это шанс занять нишу архитектора автономных систем.
Ваш план развития:
— понимание основ агентной логики и Reasoning;
— проектирование систем памяти и доступа к данным;
— создание мультиагентных пайплайнов для рабочих задач;
— интеграция агентов в бизнес-процессы компании.
Курс «Разработка ИИ-агентов» — это интенсивный путь от теории до создания сложных производственных систем.
Начать погружение в ИИ
До 19 января купите курс и заберите два дополнительных в подарок (акция «3 в 1»).IEnumerable.
Пример который встречается постоянно:
IEnumerable<Order> orders = GetOrders();
if (orders.Count() > 0)
{
// do something
}
Если внутри реально List или массив, то количество элементов доступно быстро через свойство Count или Length, то есть без прохода по данным.
Но Count() как LINQ метод обязан работать для любого IEnumerable, поэтому в общем случае он перечисляет элементы, пока не посчитает все.
Так проверка на пустоту внезапно становится полным обходом, и это особенно больно если orders это запрос с отложенным выполнением или поток данных.
А если хочется сохранить быстрый путь для коллекций, можно явно проверить интерфейс с Count:
if (orders is ICollection<Order> collection)
{
if (collection.Count > 0)
{
// fast path
}
}
else if (orders.Any())
{
// fallback
}
Если в профилировщике внезапно появился лишний проход по данным, стоит проверить где коллекция превратилась в IEnumerable и где после этого зовется Count().
📍 Навигация: Вакансии • Задачи • Собесы
🐸Библиотека шарписта
#sharp_viewusing IronPdf;
var renderer = new ChromePdfRenderer();
string html = @"
<html>
<body>
<img src='logo.png' style='width: 200px;' />
<p>Текст под картинкой</p>
</body>
</html>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("document_with_image.pdf");
Если картинка приходит из базы данных или веб-формы, удобнее встроить её прямо в HTML через Data URI:
byte[] imageBytes = File.ReadAllBytes("logo.png");
string base64 = Convert.ToBase64String(imageBytes);
string html = $@"
<html>
<body>
<img src='data:image/png;base64,{base64}' style='width: 200px;' />
<h2>Отчёт за декабрь</h2>
</body>
</html>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("report.pdf");
Если нужно добавить штамп или подпись на готовый документ, IronPdf позволяет работать со слоями:
var existingPdf = PdfDocument.FromFile("contract.pdf");
// Создаём HTML со штампом
string stampHtml = @"
<div style='position: absolute; bottom: 50px; right: 50px;'>
<img src='approved_stamp.png' style='width: 100px; opacity: 0.7;' />
</div>";
var stampPdf = renderer.RenderHtmlAsPdf(stampHtml);
// Накладываем на каждую страницу
foreach (var page in existingPdf.Pages)
{
page.AddBackgroundPdf(stampPdf);
}
existingPdf.SaveAs("contract_approved.pdf");
Частые ошибки
• Картинка не появляется — проверьте путь к файлу. IronPdf ищет относительно рабочей директории приложения. Используйте Path.GetFullPath() для отладки или встраивайте через Base64.
• Размытые изображения — убедитесь, что исходное разрешение картинки достаточное. Для печати нужно минимум 300 DPI. Если растягиваете маленькое изображение через width в CSS, оно будет мыльным.
• Огромный размер файла — IronPdf встраивает оригиналы. Оптимизируйте картинки заранее: сжимайте JPEG до 80-85% качества, для PNG используйте инструменты вроде TinyPNG.
📍 Навигация: Вакансии • Задачи • Собесы
🐸Библиотека шарписта
#sharp_viewGroupBy в LINQ удобный и выразительный, поэтому его часто тянут в любой код, где надо что то посчитать по ключу.
Проблема в том, что GroupBy решает задачу группировки, а агрегация это частный случай, и за него иногда приходится платить лишним.
Типичный паттерн выглядит так:
var totals = orders
.GroupBy(o => o.CustomerId)
.Select(g => new
{
CustomerId = g.Key,
Total = g.Sum(o => o.Amount)
})
.ToList();
Выглядит читабельно, но GroupBy внутри строит структуру групп, а значит элементы буферизуются и создаются объекты группировок, даже если в итоге нужен только итоговый Total.
Если нужна именно аккумуляция, проще и дешевле сделать один проход и складывать суммы в словарь:
var totals = new Dictionary<int, decimal>();
foreach (var order in orders)
{
if (totals.TryGetValue(order.CustomerId, out var current))
totals[order.CustomerId] = current + order.Amount;
else
totals[order.CustomerId] = order.Amount;
}
Это скучно, зато алгоритм читается буквально, один проход, явные обновления, без сюрпризов с материализацией групп.
📍 Навигация: Вакансии • Задачи • Собесы
🐸Библиотека шарписта
#sharp_viewAppWidgetProvider — следит за событиями виджета (создали, обновили, удалили)
RemoteViews — рисует интерфейс виджета
BroadcastReceiver — ловит клики и другие действия пользователя
Вы создаёте класс на C#, прописываете его в манифесте Android, рисуете layout в XML. Стандартная Android-разработка, просто на C#.
➡️ Блог Microsoft
📍 Навигация: Вакансии • Задачи • Собесы
🐸Библиотека шарписта
#il_люминаторvar youngest = people
.OrderBy(p => p.Age)
.First();
На вид код нормальный, но по факту он платит за сортировку, хотя нужна всего одна крайняя точка.
Правильнее выразить намерение напрямую через MinBy:
var youngest = people.MinBy(p => p.Age);
MinBy возвращает элемент с минимальным ключом и не требует полной сортировки последовательности. Если в коде встречается OrderBy().First() или OrderByDescending().First(), это повод остановиться и проверить, не ищется ли просто минимум или максимум.
📍 Навигация: Вакансии • Задачи • Собесы
🐸Библиотека шарписта
#sharp_viewdotnet new gitignore делает все за вас. В ней уже прописаны все типичные исключения: каталоги bin/, obj/, кэш NuGet, файлы публикаций, временные артефакты IDE и прочие служебные данные, которые не должны попадать в репозиторий.
Не нужно искать шаблон на GitHub или копировать его вручную — всё доступно из коробки.
📍 Навигация: Вакансии • Задачи • Собесы
🐸Библиотека шарписта
#лучшее_из_библиотеки_2025var collection = new BlockingCollection<int>();
// Поток-поставщик (Producer)
Task.Run(() =>
{
for (int i = 0; i < 10; i++)
{
collection.Add(i);
Console.WriteLine($"Производитель: добавил {i}");
Thread.Sleep(500);
}
collection.CompleteAdding();
});
// Поток-потребитель (Consumer)
Task.Run(() =>
{
foreach (var item in collection.GetConsumingEnumerable())
{
Console.WriteLine($"Потребитель: получил {item}");
}
}).Wait();
2️⃣ Использование TaskCompletionSource<T>
Когда нужно передать значение между потоками в будущем.
var tcs = new TaskCompletionSource<int>();
// Поток-поставщик
Task.Run(() =>
{
Thread.Sleep(2000);
tcs.SetResult(42);
});
// Поток-потребитель
int result = await tcs.Task;
Console.WriteLine($"Получено: {result}");
3️⃣ Использование ConcurrentQueue<T>
Если нужно неблокирующее хранилище данных.
var queue = new ConcurrentQueue<int>();
// Поток-поставщик
Task.Run(() =>
{
for (int i = 0; i < 10; i++)
{
queue.Enqueue(i);
Console.WriteLine($"Добавлено {i}");
Thread.Sleep(500);
}
});
// Поток-потребитель
Task.Run(() =>
{
while (true)
{
if (queue.TryDequeue(out int item))
{
Console.WriteLine($"Получено {item}");
}
Thread.Sleep(100);
}
}).Wait();
4️⃣ Использование Channel<T> (System.Threading.Channels)
Альтернативный подход к BlockingCollection<T>
var channel = Channel.CreateUnbounded<int>();
// Поток-поставщик
_ = Task.Run(async () =>
{
for (int i = 0; i < 10; i++)
{
await channel.Writer.WriteAsync(i);
Console.WriteLine($"Производитель: {i}");
await Task.Delay(500);
}
channel.Writer.Complete();
});
// Поток-потребитель
await foreach (var item in channel.Reader.ReadAllAsync())
{
Console.WriteLine($"Потребитель: {item}");
}
💬 Какой вариант используете в проде?
📍 Навигация: Вакансии • Задачи • Собесы
🐸Библиотека шарписта
#лучшее_из_библиотеки_2025Thread из пространства имен System.Threading. Поток проходит несколько этапов на протяжении своего жизненного цикла. Давайте разберём эти этапы.
— Unstarted. Не запущен
Поток создан, но ещё не запущен. Он находится в этом состоянии сразу после инициализации объекта Thread, но до вызова метода Start().
Thread thread = new Thread(MyMethod);
// Поток создан, но не запущен
— Running. Выполняется
Поток начинает выполнение после вызова Start(). В этом состоянии поток выполняет код, переданный в качестве делегата.
thread.Start();
// Поток запущен и выполняется
— Waiting. Ожидание
Поток приостанавливается, ожидая выполнения какого-либо условия или ресурса. Это может быть вызвано методами:
• Thread.Sleep() — поток засыпает на заданное время.
• Monitor.Wait() или lock — поток ожидает захвата монитора.
• Thread.Join() — поток ожидает завершения другого потока.
Thread.Sleep(1000);
// Поток приостановлен на 1 секунду
— Blocked. Заблокирован
Поток заблокирован, пытаясь получить доступ к ресурсу, уже занятому другим потоком. Например, ожидание захвата блокировки через lock.
— Stopped. Остановлен
Поток завершает выполнение. Это состояние наступает, когда метод, выполняемый в потоке, завершает работу или вызывается устаревший метод Abort(). Поток в этом состоянии больше нельзя запустить снова.
thread.Join();
// Основной поток ожидает завершения
Дополнительные состояния
— Background. Фоновый поток
Поток может быть фоновым, если свойство IsBackground установлено в true. Фоновые потоки завершаются автоматически, когда завершается основной поток приложения.
thread.IsBackground = true;
// Устанавливаем поток как фоновый
— Suspended. Приостановлен (устарело)
Метод Suspend() приостанавливал выполнение потока, но был удалён из новых версий .NET из-за возможных проблем с безопасностью.
📍 Навигация: Вакансии • Задачи • Собесы
🐸Библиотека шарписта
#лучшее_из_библиотеки_2025docker run -d --hostname my-rabbit --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:management
2️⃣ Настраиваем MassTransit
Необходимо зарегистрировать MassTransit и подключить его к RabbitMQ:
builder.Services.AddMassTransit(x =>
{
x.UsingRabbitMq((context, cfg) =>
{
cfg.Host("localhost", "/", h =>
{
h.Username("guest");
h.Password("guest");
});
});
});
Этот код создает подключение к RabbitMQ, которое позволяет сервису отправлять и получать сообщения.
3️⃣ Создаем производителя
Производитель отправляет сообщения в очередь, используя MassTransit:
public async Task SendMessage(IBus bus, string message)
{
var endpoint = await bus.GetSendEndpoint(new Uri("queue:order-queue"));
await endpoint.Send(new OrderCreated { OrderId = Guid.NewGuid(), Message = message });
}
4️⃣ Создаем потребителя
Потребитель автоматически получает и обрабатывает сообщения из очереди:
public class OrderConsumer : IConsumer<OrderCreated>
{
public Task Consume(ConsumeContext<OrderCreated> context)
{
Console.WriteLine($"Получено сообщение: {context.Message.Message}");
return Task.CompletedTask;
}
}
💾 Сохраняйте мини-гайд себе, чтобы не потерять
📍 Навигация: Вакансии • Задачи • Собесы
🐸Библиотека шарписта
#лучшее_из_библиотеки_2025await несколько раз для одного и того же ValueTask нельзя, так как это может привести к неожиданным результатам:
ValueTask<int> task = GetValueAsync();
int value1 = await task;
int value2 = await task; // Ошибка
• Конвертация в Task
Eсли требуется передать ValueTask в API, которое ожидает Task, можно вызвать метод .AsTask():
Task<int> task = GetValueAsync().AsTask();
• Когда использовать ValueTask:
+ Операция часто завершается синхронно.
+ Создание объекта Task может быть слишком накладным.
+ Вы пишете библиотеку с высокой производительностью.
• Когда НЕ использовать ValueTask:
- Операция всегда асинхронна.
- Производительность не является критически важной.
- Приложение не оптимизировано под работу с ValueTask.
📍 Навигация: Вакансии • Задачи • Собесы
🐸Библиотека шарписта
#лучшее_из_библиотеки_2025
现已上线!2025 年 Telegram 研究 — 年度关键洞察 
