23284
Все самое полезное для C#-разработчика в одном канале. По рекламе: @proglib_adv Учиться у нас: https://proglib.io/w/b60af5a4 Для обратной связи: @proglibrary_feeedback_bot РКН: https://gosuslugi.ru/snet/67a5c81cdc130259d5b7fead
🎄 Социальный опрос айтишников
Есть два типа людей: те, кто ставит ёлку в начале декабря и наслаждается атмосферой целый месяц, и те, кто украшает её 31-го под бой курантов.
💬 Скидывайте фото своих ёлок в комменты 👇
📍 Навигация: Вакансии • Задачи • Собесы
🐸Библиотека шарписта
#entry_point
📅 Даты и время в .NET без сюрпризов
В .NET есть разные типы под разные задачи, и из этого обычно и растут ошибки. DateTime это просто дата и время, но без явного смещения. DateTimeOffset хранит дату, время и смещение от UTC, поэтому однозначно задает момент времени.
Для часовых поясов используйте TimeZoneInfo, Microsoft прямо пишет, что для новой разработки лучше TimeZoneInfo вместо старого TimeZone.
var nowUtc = DateTimeOffset.UtcNow;
var moscow = TimeZoneInfo.FindSystemTimeZoneById("Europe/Moscow");
var nowMoscow = TimeZoneInfo.ConvertTime(nowUtc, moscow);
Console.WriteLine(nowUtc);
Console.WriteLine(nowMoscow);
TimeZoneInfo умеет переводить время между часовыми поясами, а DateTimeOffset делает момент переносимым между машинами и окружениями.DateOnly для календарной даты без времени, например отпуск или дата рождения. TimeOnly для времени без даты, например расписание.var vacation = new DateOnly(2026, 1, 10);
var standup = new TimeOnly(10, 30);
Console.WriteLine(vacation);
Console.WriteLine(standup);
DateOnly и TimeOnly заменяют костыли с DateTime и TimeSpan, когда вам не нужен момент времени.
📎 Методы вместо сеттеров
Открытые геттеры и сеттеры кажутся удобными, но позволяют внешнему коду разрушить внутреннюю логику объекта. Можно предоставлять методы, которые гарантируют соблюдение правил.
Например, в заказе количество позиций не должно быть отрицательным. Но с сеттером любой вызовет orderItem.setQuantity(-1000). Объект становится некорректным, а баги проявятся позже.
Создайте метод, который принимает ID товара и новое значение. Внутри проверьте, что число положительное, обновите поле и пересчитайте сумму.
Пример:
public void ChangeQuantity(string itemId, int newQuantity) {
if (newQuantity < 0) throw new ArgumentException("Количество не может быть отрицательным");
var item = items.FirstOrDefault(i => i.Id == itemId);
if (item != null) {
item.Quantity = newQuantity;
RecalculateTotal();
}
}
📂 Files для Windows 11 получил интеграцию с PowerToys
Files это популярный сторонний файловый менеджер для Windows 11. В новости пишут, что он получил интеграцию с одной из самых удобных утилит PowerToys, которая делает быстрый предпросмотр файлов.
Речь про Peek. Вы выделяете файл и жмёте Ctrl плюс Space, и файл открывается в лёгком окне предпросмотра. Это работает как быстрый просмотр, без запуска отдельного приложения.
➡️ Источник
📍 Навигация: Вакансии • Задачи • Собесы
🐸Библиотека шарписта
#async_news
🌐 Постоянные редиректы в ASP.NET
RedirectPermanent работает в контроллерах MVC и Core. Метод отправляет статус 301 Moved Permanently, браузеры кэшируют такой редирект и сразу переходят на новый адрес без повторных запросов к старому.
В контроллере это выглядит просто:
public IActionResult OldPage()
{
return RedirectPermanent("/new-page");
}
Redirect в статусе ответа. Redirect (302) говорит «временно перемещено», браузер каждый раз проверяет старый URL. RedirectPermanent (301) фиксирует «навсегда», что экономит трафик и улучшает SEO.
👨💻 Рабочее место админа пепси-кодера
📍 Навигация: Вакансии • Задачи • Собесы
🐸Библиотека шарписта
#garbage_collector
🌐 Перенаправляем запросы в ASP.NET
Redirect в ASP.NET перенаправляет пользователя на другой URL с кодом 302 Found. Это временное перемещение, когда браузер каждый раз проверяет исходный адрес.
Основные примеры использования:
// 1. Абсолютный путь
return Redirect("https://example.com");
// 2. Относительный путь
return Redirect("/admin/dashboard");
// 3. Действие контроллера
return RedirectToAction("Edit", "User", new { id = model.Id });
// 4. URL с параметрами
return Redirect($"/products?category={categoryId}");
🔗 Цепочка конструкторов в C#
Конструкторы вызываются при создании объекта. В C# можно связывать их в цепочку: один конструктор вызывает другой. Это убирает дублирование кода инициализации.
Представьте класс User с тремя конструкторами:
public class User
{
public string Name { get; set; }
public string Email { get; set; }
public int Age { get; set; }
public DateTime Created { get; set; }
// Базовая инициализация
public User()
{
Created = DateTime.Now;
Name = "Гость";
Email = "guest@example.com";
}
public User(string name)
{
Created = DateTime.Now; // Дублируем!
Name = name;
Email = "guest@example.com";
}
public User(string name, string email)
{
Created = DateTime.Now; // Снова дублируем!
Name = name;
Email = email;
}
}
: this(параметры):public class User
{
public string Name { get; set; }
public string Email { get; set; }
public int Age { get; set; }
public DateTime Created { get; set; }
// Главный конструктор - вся логика здесь
public User(string name, string email, int age = 0)
{
Created = DateTime.Now;
Name = name ?? "Гость";
Email = email ?? "guest@example.com";
Age = age;
}
// Цепочка к главному
public User() : this("Гость", "guest@example.com") { }
public User(string name) : this(name, "guest@example.com") { }
public User(string name, string email) : this(name, email, 0) { }
}
var user1 = new User(); // Гость, guest, 0
var user2 = new User("Иван"); // Иван, guest, 0
var user3 = new User("Иван", "i@example"); // Иван, i@example, 0
var user4 = new User("Анна", "a@ex", 25); // Анна, a@ex, 25
Created = Now автоматическиpublic User(string name) : this(name, "default") // OK
{
// Дополнительная логика
}
public User() : this("a") : base() // Ошибка!
⏳ Интерполяция строк не бесплатна
Строки в горячих участках вашего кода бьют по памяти сильнее чем кажется. В ASP.NET они часто оказываются в топе аллокаторов в профайлере.
Что создаёт лишние строки
• Интерполяцияvar key = $"{userId}:{tenantId. Каждый вызов порождает новый string и новый массив char.
• string.Formatstring.Format("{0}-{1}", a, b); работает по той же схеме создаёт новый объект и буфер под символы.
• Частые ToString()request.Headers["X-Id"].ToString(); при каждом запросе снова строит строку из заголовка.
Как сделать безопаснее
• Использовать структурированное логирование
logger.LogInformation("User {UserId} logged in", userId);ILogger не формирует строку когда соответствующий уровень логов отключён, а значения попадают в структурированные поля записи.ToString() без необходимостиSpan и ValueStringBuilder
WANTED: .NET-РЕЦЕДИВИСТ
Разыскивается эксперт, способный приручить C# и заставить его работать идеально. Мы знаем, что у тебя есть секретные приёмы, которыми пора поделиться с комьюнити.
Приметы:
— глубоко знает экосистему .NET и может сравнить её с Java или Go;
— умеет проектировать сложные системы без «костылей»;
— готов выступать на большую аудиторию в качестве автора или ментора;
— хочет монетизировать свой профессиональный опыт.
Что в контракте:
— гонорар за интеллектуальный вклад;
— статус официального эксперта Proglib Academy;
— заметный буст личного бренда.
Признаться во всём здесь
P.S. Знаешь сильного «шарписта»? Сдай его следствию — перешли пост.
😊 Потоковый ответ вместо буферизации
Во многих API до сих пор пишут методы вида public IEnumerable<OrderDto> GetOrders() и думают что это ленивый возврат данных. На деле фреймворк вынужден сначала полностью пройти по IEnumerable, собрать коллекцию, а уже потом целиком сериализовать ее в JSON и отправить в сеть.
Большой список целиком оказывается в куче, всплеск аллокаций ускоряет сборку мусора, а клиент ждет первый байт до тех пор пока не будет готов весь массив данных.
В современных версиях .NET намного безопаснее отдавать последовательность как IAsyncEnumerable<T>. В этом случае ASP.NET может сериализовать элементы по мере их появления и выводить их в ответ потоком вместо полной буферизации результата.
Альтернативный вариант — явно использовать Results.Stream и JsonSerializer.SerializeAsync(stream, data) чтобы отдать JSON как поток байтов и держать в памяти лишь небольшой кусок данных.
📍 Навигация: Вакансии • Задачи • Собесы
🐸Библиотека шарписта
#il_люминатор
🥹🥹 Можно заказать на амазоне
📍 Навигация: Вакансии • Задачи • Собесы
🐸Библиотека шарписта
#garbage_collector
⚙️ Поиск дубликатов с LINQ
Когда коллекция растет, руками гонять двойные циклы для поиска дубликатов уже не хочется. В .NET это хорошо решается комбинацией LINQ и HashSet, плюс классическими GroupBy и Distinct.
• LINQ Any и HashSet
Идея простая: HashSet хранит только уникальные элементы. Метод Add возвращает false если такое значение уже было. Значит можно пробежать коллекцию через Any и остановиться на первом дубликате:
public static bool HasDuplicatesAny<T>(IEnumerable<T> source)
{
HashSet<T> seen = new();
return source.Any(item => !seen.Add(item));
}
true как только встретится элемент который не удалось добавить в набор.GroupBy. Мы группируем по значению и фильтруем группы в которых больше одного элемента:public static IEnumerable<T> GetDuplicatesGroupBy<T>(IEnumerable<T> source)
{
return source
.GroupBy(x => x)
.Where(g => g.Count() > 1)
.Select(g => g.Key);
}
Distinct:public static bool HasDuplicatesDistinct<T>(IEnumerable<T> source)
{
int total = source.Count();
int unique = source.Distinct().Count();
return unique != total;
}
Any + HashSet. GroupBy удобно когда нужны сами дубликаты.
AI-агенты в продакшн: промышленный стандарт архитектуры
В Enterprise-разработке на первый план выходит контроль и предсказуемость. Мы учим проектировать ИИ-агентов как надёжные системы со строгой логикой и мониторингом.
Ключевые модули:
— управление состоянием в LangGraph для проектирования сложных стейт-машин;
— архитектура `RAG` на базе Pinecone или Chroma для обоснованной генерации ответов;
— промышленная автоматизация через n8n для интеграции логики в бизнес-процессы;
— безопасность и observability с внедрением LangSmith и Guardrails для аудита системы.
Результат — масштабируемая система, готовая к интеграции в ваш стек.
Подробности курса
🤨 Lazy<T> против всех синглтонов
Lazy<T> в C# стал де-факто стандартом для thread-safe ленивой инициализации. Забудьте lock, volatile и двойные проверки.
Проблемы классического синглтона:
public sealed class ClassicSingleton
{
private static ClassicSingleton _instance;
private static readonly object _lock = new object();
private ClassicSingleton()
{
// 2 секунды на реальную БД/файлы
Thread.Sleep(2000);
Console.WriteLine("ClassicSingleton создан");
}
public static ClassicSingleton Instance
{
get
{
if (_instance == null)
{
lock (_lock)
{
_instance ??= new ClassicSingleton();
}
}
return _instance;
}
}
}
public sealed class LazySingleton
{
private static readonly Lazy<LazySingleton> _lazy = new Lazy<LazySingleton>(() =>
{
Console.WriteLine("LazySingleton создан (ТОЛЬКО РАЗ)");
Thread.Sleep(2000); // Имитация тяжёлой работы
return new LazySingleton();
});
private LazySingleton() { }
public static LazySingleton Instance => _lazy.Value;
public static bool IsInitialized => _lazy.IsValueCreated;
}
static async Task TestPerformance()
{
var stopwatch = Stopwatch.StartNew();
var tasks = Enumerable.Range(0, 1000)
.Select(i => Task.Run(() =>
{
var instance = LazySingleton.Instance;
return instance.GetHashCode();
}))
.ToArray();
var results = await Task.WhenAll(tasks);
stopwatch.Stop();
Console.WriteLine($"1000 потоков: {stopwatch.ElapsedMilliseconds}ms");
Console.WriteLine($"Уникальных объектов: {results.Distinct().Count()}");
Console.WriteLine($"Инициализировано: {LazySingleton.IsInitialized}");
}
Lazy<T> — это не хак, а официальный стандарт Microsoft.
📍 Навигация: Вакансии • Задачи • Собесы
🐸Библиотека шарписта
#garbage_collector
🤔 Не знаете, в чём встречать Новый год? Не беда!
Tproger как раз собрал стек новогодних нарядов, чтобы вы сияли ярче, чем любая гирлянда. Скорее заходите в виртуальную примерочную и выбирайте скин! 💃
Реклама. ИП Михайлишина Гузель Фаниловна.
ИНН 165042823905. Erid 2VtzqukJLdp
👨💻 Ложная иммутабельность
Часто новички в функциональном программировании думают, что сделали иммутабельный код. Берут список, вызывают ToList() или Select() и радуются новому списку. Но в C# это ловушка.
Проблема в ссылочных типах. var copy = original.ToList() создаёт новый List, но все элементы внутри — те же самые объекты. Меняете свойство через copy, и оригинал тоже меняется.
Правило трёх
• Records с init-only свойствами для value-like поведения
• ImmutableList<T> из System.Collections.Immutable — настоящие неизменяемые коллекции
• Struct только для маленьких типов
📍 Навигация: Вакансии • Задачи • Собесы
🐸Библиотека шарписта
#sharp_view
⛄️ Путь к мечте продолжается
Пока вы думаете, что «не хватает времени» или «сложные обстоятельства» — Стас перенёс две нейрохирургические операции, собирает себя по кусочкам, при этом проходит курс по C++, пилит проекты для портфолио и откликается на вакансии.
➡️ Читать продолжение пути Стаса
📍 Навигация: Вакансии • Задачи • Собесы
🐸Библиотека шарписта
AI-агенты: почему LangChain и ReAct — это must-have для шарписта 🛠️
Интеграция ИИ в корпоративный софт больше не ограничивается вызовом API. Нужно строить сложные системы с памятью и автономной логикой.
На курсе мы разберём передовой стек разработки мультиагентных систем.
Ключевые темы:
— реализация логики «Reasoning + Acting» для полной автономности;
— использование n8n как мощного визуального оркестратора процессов;
— применение протокола MCP от Anthropic для надёжной связи;
— построение масштабируемого RAG для работы с Big Data.
Создайте систему из нескольких агентов, которая возьмёт на себя сложную аналитику и поддержку клиентов.
Старт курса 🚀
👥 Дайджест 2025
Год завершается, самое время подвести итоги. 2025 принёс нам .NET 10 — юбилейный LTS-релиз.
↪️ .NET 10
Релиз состоялся 11 ноября 2025 года. Сами Microsoft называет его самым производительным, современным и безопасным выпуском платформы.
• Улучшения JIT-компилятора: инлайнинг, девиртуализация методов, оптимизация генерации кода для struct-аргументов.
• Поддержка AVX10.2 для Intel и Arm64 SVE для продвинутой векторизации.
• Улучшения NativeAOT: меньше размер, быстрее запуск.
• Стековая аллокация для маленьких массивов фиксированного размера.
• Встроенная поддержка AI через новый Microsoft Agent Framework
• Постквантовая криптография: добавлена поддержка алгоритмов ML-KEM (FIPS 203), ML-DSA (FIPS 204) и SLH-DSA (FIPS 205).
↪️ C# 14
C# 14 обновили большим количеством фич: ключевое слово field, блоки расширений, partial-конструкторы и события, оператор null-conditional assignment, перегрузку составных операторов присваивания, модификаторы параметров в лямбдах без указания типа.
↪️ F# 10
У нас был цикл постов про обновления F#. От подавления предупреждений до авто-свойств.
↪️ Entity Framework Core 10
• Полная поддержка типа данных vector и функции VECTOR_DISTANCE() для Azure SQL Database и SQL Server 2025
• EF Core 10 добавляет first-class поддержку операторов LeftJoin и RightJoin
• Теперь можно назначать имена фильтрам и управлять ими по отдельности
↪️ ASP.NET 10 и Blazor
• Декларативное сохранение состояния через атрибут [PersistentState]
• Circuit state persistence — пользователи могут возобновить сессию после дисконнекта.
• blazor.web.js теперь сжимается: размер уменьшился на 76% — со 183 KB до ~43 KB.
• Встроенная страница NotFound.razor в шаблонах.
• Полная поддержка OpenAPI 3.1 и YAML-формата для API-документации.
↪️ .NET MAUI 10
• XAML компилируется во время сборки, а не парсится в runtime
• Новый шаблон проекта для интеграции MAUI с .NET Aspire
• Экспериментальная возможность использовать CoreCLR вместо Mono
.NET Aspire
• Команды aspire new, aspire run, aspire config и preview-команды aspire publish/deploy
• Можно определить Aspire AppHost без традиционного project file
• Типизированные клиентские библиотеки для GitHub Models, Azure AI Foundry и OpenAI
↪️ Visual Studio 2026
• Более 5000 исправленных багов и 300 реализованных фич.
• Запуск приложений до 30% быстрее по сравнению с VS 2022
• IDE отвязана от build tools — можно обновлять IDE без влияния на toolchain
• Полная поддержка .NET 10 и C# 14
Год получился насыщенным. Все события можно найти по хэштегу #async_news.
📍 Навигация: Вакансии • Задачи • Собесы
🐸Библиотека шарписта
#async_news
🛍 Microsoft Store из терминала
Windows 11 получила встроенный CLI для Microsoft Store. Команда store ищет, устанавливает и обновляет приложения из Store прямо из командной строки. Быстрее winget и с поддержкой платных приложений.
Команда store покажет все команды. Работает из коробки на Windows 11 версии preview v22511.1401.5.0.
Основные команды
Поиск приложений:
store search vlc
store search "notepad++"
store search powertoys
store similar vlc
store install vlc
store install 9NBLGGH4NNS1 # ProductId
store installed
store updates
store upgrade vlc
store upgrade --all
😑 Что выведет код
Делегаты в шарпах — штука базовая, но в деталях легко запутаться. Посмотрите на код и предскажите вывод, прежде чем проверять.
➡️ Для любознательных ответ в нашем канале с задачами
📍 Навигация: Вакансии • Задачи • Собесы
🐸Библиотека шарписта
#dotnet_challenge
🛠 ToonEncoder для .NET
ToonEncoder — это библиотека, которая кодирует данные в TOON. TOON описывается как компактное человекочитаемое представление той же модели данных что и JSON, но с упором на экономию токенов в промптах для LLM.
Практический кейс для айтишников это подготовка данных для LLM, например список логов, результатов поиска, объектов из базы, или пачка тул коллов, где табличный вид дает меньше лишней пунктуации и меньше повторяющихся ключей.
Пример:
using Cysharp.AI;
var users = new User[]
{
new (1, "Alice", "admin"),
new (2, "Bob", "user"),
};
string toon = ToonEncoder.Encode(users);
Console.WriteLine(toon);
string toon2 = ToonEncoder.EncodeAsTabularArray(users);
Console.WriteLine(toon2);
Console.WriteLine(toon == toon2); // same result
public record User(int Id, string Name, string Role);
⚙️ Yield в упрощает итерацию
Yield позволяет создавать итераторы, возвращая элементы коллекции по одному, без загрузки всего в память сразу.
Как работает yield return
При yield return код возвращает значение и сохраняет состояние для следующей итерации в foreach или LINQ. Компилятор генерирует класс, реализующий IEnumerable<T>, с MoveNext() для продолжения.
Пример простого генератора четных чисел:
IEnumerable<int> EvenNumbers(int max) {
for (int i = 0; i <= max; i += 2) {
yield return i;
}
}0, 2, 4... лениво, только при запросе.
🎅 Секретный Санта для айтишников от Proglib.academy
Весь этот год команда Академии запускала курсы для айтишников. А под Новый год мы запускаем новый курс по ИИ-агентам и ставим под ёлку самый свежий стек 2025 года и обучение проектированию автономных нейросетевых экосистем — от LLM и ReAct-циклов до мультиагентных систем, LangGraph, AutoGen и продакшн-практик.
🎁 Хотим дарить подарки и приглашаем вас поучаствовать в конкурсе:
1️⃣ Упомяните курс Академии у себя в блоге.
2️⃣ Пришлите скрин сюда.
3️⃣ Получите секретный промокод на 10 000 ₽ при оплате любого курса.
Подходит всё — соцсети, блоги, Telegram-каналы от 300 подписчиков и более.
🎄 Акция действует до Нового года.
Win-win, всё как мы любим!
🎚 Подборка вакансий для шарпистов
Backend-разработчик C# на удалёнку.
.NET Developer (Middle) — удалёнка или офис в СПб.
Senior Backend-разработчик — от 365 000 ₽
➡️ Еще больше топовых вакансий — в нашем канале C# Jobs
🐸 Библиотека шарписта
🛠 RoomSharp 0.4.3: миграции для продакшена
RoomSharp смешивает декларативные [AutoMigration] и императивные классы миграций. Автоматически обрабатывает переименования таблиц, колонок, добавление/удаление полей. MigrationManager следит за версиями в __room_metadata, предупреждает о деструктивных изменениях и запускает колбэки.
Установка — dotnet add package RoomSharp плюс провайдеры вроде RoomSharp.SqlServer. Расширения RoomSharp.Extensions для конфига.
📎 В блоге разработчиков много полезной информации
📍 Навигация: Вакансии • Задачи • Собесы
🐸Библиотека шарписта
#async_news
🗂 Работа и пет-проекты с одного ПК
Работаете днём в корпоративном репо, вечером в личном на GitHub. Один ПК, но email и ключи подписи разные. Ручная смена настроек Git каждый раз бесит и приводит к косякам с коммитами.
Условные includeIf решают всё
Добавьте в ~/.gitconfig пару строк. Git сам подхватит нужный конфиг по пути к папке или remote URL.
Для рабочих папок загрузит корпоративный email:
[includeIf "gitdir:~/work/"] path = .gitconfig-corp
[includeIf "hasconfig:remote.*.url:https://github.com/вашеимя/**"] path = .gitconfig-personal
git config --global user.email каждый раз.
🧠 When и Unless для умных проверок
В FluentValidation условная валидация запускает правила только при нужных условиях. When и Unless экономят циклы и делают валидаторы читаемыми.
• When = «проверить, если условие верно»
• Unless = «проверить, если условие НЕ верно»
Пример:
RuleFor(x => x.ShippingAddress)
.NotEmpty()
.When(x => x.DeliveryMethod == "Express");
RuleFor(x => x.CreditCard)
.NotEmpty()
.Unless(x => x.PaymentMethod == "PayPal");