Когда выбирать struct/record struct вместо class?
Для маленьких, неизменяемых и часто копируемых значений (≤ ~16–32 байт), где важна локальность и отсутствие аллокаций. Нужны структурное равенство/with — берите record struct. Если объект крупный/мутабельный/долго живёт — используйте class.
Библиотека собеса по С#
Альфа-Банк ищет скиллового коллегу QA Fullstack на C#
Полная удалёнка или гибрид в Москве, Питере и Екатеринбурге.
Что нужно делать:
— Разрабатывать автотесты на C#;
— Запускать функциональные, регрессионные и интеграционные тесты, включая API;
— Снижать количество дефектов в диалоге с разработкой и бизнесом.
Навыки:
— Опыт автоматизации на C# и ручного тестирования от года;
— Знание API (SOAP, REST), SQL, основ клиент-серверной архитектуры и тест-дизайна.
Условия и развитие:
— Сильное сообщество QA и C#-разработчиков: можно получить совет и поделиться опытом на митапе и в блоге на Хабре:
— Реальная перспектива роста: технический трек развития, в том числе до C#-лида.
Оставляйте отклик по ссылке.
👀 Как вы смотрите видео?
Разбираете урок, слушаете интервью фоном или начинаете на телефоне, а продолжаете на большом экране?
Хотим узнать, что делает просмотр удобным для вас. Вспомните последнее видео — ролик, урок, фильм или серию сериала — и расскажите о своём опыте. Если детали не запомнились, можно так и ответить.
🕰️ Опрос займёт около 3 минут.
В результатах сможете сравнить свои привычки с ответами других участников. А разработчики и инженеры видеоплатформы расскажут, что происходит по ту сторону экрана, пока вы смотрите видео.
🔗 Поделиться опытом
🐸 Библиотека программиста
Можно ли после Cancel вернуть CancellationTokenSource в рабочее состояние через TryReset?
Нет. Если отмена уже запрошена, TryReset возвращает false. Это не кнопка «отменить отмену». Проверка на .NET 8 подтвердила false после Cancel, а для свежего источника вернула true.
Метод предназначен для единственного владельца источника: предыдущая операция завершена, никто больше не должен запросить её отмену, оставшиеся регистрации больше не нужны. Успешный reset удаляет регистрации.
Одновременный вызов Cancel не является потокобезопасным сценарием. Также нельзя считать старый токен отдельным поколением: он всё ещё связан с тем же источником. Для независимых операций проще создавать отдельные источники отмены.
Контракт TryReset
Библиотека собеса по C#
WaitAsync завершился по тайм-ауту. Исходная Task тоже остановилась?
Нет, сам WaitAsync ограничивает ожидание. В проверке на .NET 8 исходная задача возвращала 42 после задержки. Ожидание с коротким тайм-аутом выбросило TimeoutException, но последующий await исходной задачи всё равно получил 42.
Чтобы остановить работу, она должна поддерживать отмену: токен передают в саму операцию, а операция проверяет его или использует API, который это делает. Токен, переданный только в WaitAsync, не добавляет такой поддержки исходному коду.
Поэтому после тайм-аута надо решить, кто отвечает за продолжающуюся операцию, её ресурсы и возможное позднее исключение. «Перестали ждать» и «работа завершилась» — разные состояния.
Контракт Task.WaitAsync
Библиотека собеса по C#
Импульс Т1: возвращаемся с главным ИТ-событием осени ⚡️
19 ноября в Москве состоится одна из самых значимых ИТ-конференций года — Импульс Т1. Это масштабная площадка для обмена практическим опытом и формирования актуальных инженерных навыков.
Мероприятие объединит более 2000 участников ИТ-отрасли: программистов, архитекторов, инженеров, лидеров технологического рынка — всех, кто создает цифровое будущее.
Вас ждет:
🔹5 направлений деловой программы;
🔹130+ спикеров со всей страны;
🔹70+ активностей от практиков индустрии.
Главная особенность года — «практические тренажеры», когда каждый участник формирует свой маршрут и сразу отрабатывает теорию на практике:
🔘Код-ретрит — развитие навыков парного программирования, разработка через тестирование и фокус на качестве кода в реальных условиях;
🔘Ресурсные игры — командная работа с ограничениями ресурсов и времени, практика решений конфликтов и ретроспективы;
🔘Зона арх-каты — стресс-тест архитектуры на прочность, поиск слабых мест и интерактивная карта решений;
🔘Стенд доступности — практическое погружение в цифровую доступность и опыт взаимодействия людей с ОВЗ с ИТ-сервисами.
Зачем участвовать?
✔️ Получить реальные инструменты для ежедневной работы.
✔️ Показать экспертизу на деле и обменяться опытом с ведущими инженерами.
✔️ Быть в курсе ключевых трендов и опережать индустрию.
✔️ Стать частью сообщества технологических лидеров отрасли.
📆 Дата: 19 ноября 2026 года.
📍 Где встречаемся: г. Москва, ул. Волочаевская, д. 48, стр. 1, Дворец культуры завода «Серп и молот» (LOFT#8).
Получите доступ к опыту лидеров отрасли и новым ИТ-решениям, регистрируйтесь по ссылке.
О рекламодателе
Чем init отличается от set и делает ли он объект неизменяемым?
Ответ: Нет, init сам по себе не делает весь объект неизменяемым.
Свойству с set можно присваивать значение после создания объекта, если позволяет доступ. init ограничивает присваивание этапом инициализации: например, значение можно задать в инициализаторе объекта, а обычное присваивание после этого компилятор запретит.
Но если свойство хранит List<string>, вызов Add по-прежнему допустим. init запрещает заменить ссылку на список после инициализации, а не менять содержимое списка.
На собеседовании стоит разделять две вещи: возможность присвоить свойству новое значение и возможность изменить объект, на который оно ссылается. init появился в C# 9.
Подробнее в документации Microsoft Learn
🐸 Библиотека собеса по C#
😎 Выбираешь один курс, второй получаешь бесплатно. Баг? Нет, акция!
До 31 октября в Proglib Academy можно выбрать два курса и заплатить только за один.
Например, можно совместить:
💥 ИИ для разработчиков — применение нейросетей в повседневной разработке: работа с кодом, тестирование и ревью.
💥 Разработка ИИ-агентов — архитектура агентов и создание собственных AI-решений.
Вместе курсы стоят 108 000 ₽, но по акции оба обойдутся в 59 000 ₽. Экономия — 49 000 ₽ 🔥
Интересуют Python и ИИ или алгоритмы и архитектура? Можно выбрать любые два курса из каталога.
Акция действует до 31 октября включительно ⏳
⏬ Собрать свой учебный билд
Гарантирует ли required, что свойство не будет равно null?
Ответ: Нет. required требует инициализации члена при создании объекта, но не проверяет его значение во время выполнения.
Для свойства required string Name { get; init; } выражение new User() вызовет ошибку компиляции. А new User { Name = null } удовлетворит требованию required. При включённом анализе nullable компилятор выдаст предупреждение о присваивании null; настройки проекта могут превращать такие предупреждения в ошибки.
Атрибут SetsRequiredMembers тоже не доказывает, что конструктор всё заполнил: компилятор доверяет этому обещанию автора.
Поэтому required не заменяет проверку аргументов и правил предметной области. Модификатор доступен с C# 11.
Подробнее в документации Microsoft Learn
🐸 Библиотека собеса по C#
Защищает ли оператор ! от NullReferenceException?
Ответ: Нет. Постфиксный ! подавляет предупреждения nullable-анализа для выражения, но ничего не проверяет во время выполнения.
Если переменная value содержит null, обращение value!.Length всё равно завершится NullReferenceException. Оператор не создаёт объект, не подставляет значение и не добавляет проверку.
Использовать ! стоит, когда у разработчика есть основание считать значение ненулевым, а компилятор этого не видит. Если значение действительно может отсутствовать, нужна обработка этого случая: проверка null, условный доступ ?. или подходящее исключение.
Не путайте постфиксный value! с логическим отрицанием !flag. Подавление nullable-предупреждений доступно с C# 8.
Подробнее в документации Microsoft Learn
🐸 Библиотека собеса по C#
Распространяется ли checked на арифметику внутри вызванного метода?
Ответ: Нет. Контекст checked действует на операции, записанные внутри него, и не переносится через вызов в тело другого метода.
Допустим, метод AddOne(int x) возвращает x + 1. Если обернуть вызов AddOne(int.MaxValue) в checked, это само по себе не заставит сложение внутри AddOne выбросить OverflowException.
Поведение сложения определяется контекстом в самом методе и настройками проверки переполнения при его компиляции. Чтобы явно проверять эту операцию, нужно написать checked(x + 1) в AddOne.
Поэтому без знания настроек компиляции нельзя уверенно обещать конкретный результат исходного примера. Важный принцип: checked не включает проверку во всём стеке вызовов.
Подробнее в документации Microsoft Learn
🐸 Библиотека собеса по C#
📌 Один курс — хорошее вложение в себя. Два по той же цене — ещё лучше.
В Proglib Academy действует акция: покупаешь один курс, а второй получаешь в подарок.
Например, можно совместить два направления по работе с ИИ:
💥 ИИ для разработчиков — применение нейросетей в повседневной разработке: работа с кодом, тестирование и ревью.
💥 Разработка ИИ-агентов — архитектура агентов и создание собственных AI-решений.
📍 Разница в цене — почти 50 тысяч рублей. Можно освоить больше, не выходя за рамки запланированного бюджета.
А если тебе интереснее Python, алгоритмы, backend или DevOps — выбирай другую пару курсов из каталога.
Акция действует до 31 октября включительно ⏳
⏮️ Пора прокачать свой стек
Чем catch с when отличается от проверки if внутри catch?
Ответ: Фильтр when определяет, подходит ли обработчик, ещё до раскрутки стека. Условие if выполняется уже после входа в catch.
В конструкции catch (Exception ex) when (CanHandle(ex)) обработчик будет выбран только при истинном результате фильтра. Если фильтр вернул false, поиск подходящего catch продолжается. В том числе может сработать следующий catch у того же try.
Если же проверять условие внутри catch, исключение уже перехвачено. Чтобы передать его дальше, придётся явно выполнить throw; — к следующим catch того же try выполнение уже не перейдёт.
При этом throw; сохраняет исходный стек исключения. Его не стоит заменять на throw ex;, который меняет информацию о стеке.
Подробнее в документации Microsoft Learn
🐸 Библиотека собеса по C#
Будет ли повторно вызван статический конструктор после исключения?
Ответ: Нет. Если статический конструктор завершился необработанным исключением, среда выполнения не запускает его заново для того же загруженного типа.
Тип остаётся неинициализированным. Обращение, требующее его инициализации, обычно приводит к TypeInitializationException; исходную причину можно посмотреть в InnerException. Повторный вызов такого обращения не становится повторной попыткой выполнить конструктор.
Поэтому временно недоступный сервер или файл — плохая зависимость для статического конструктора. Даже если ресурс появится позже, автоматического восстановления типа не произойдёт.
Если операции нужны повторные попытки, их следует организовать отдельно от статической инициализации.
Подробнее в документации Microsoft Learn
🐸 Библиотека собеса по C#
Где искать все исключения, если несколько задач в Task.WhenAll завершились с ошибкой?
Ответ: В свойстве Exception задачи, которую вернул Task.WhenAll. Оно содержит AggregateException с ошибками завершившихся задач. При await обычно выбрасывается одно из этих исключений, поэтому одного пойманного исключения недостаточно, чтобы узнать обо всех сбоях. Сохраните общую задачу в переменную, дождитесь её и при ошибке проверьте Exception.InnerExceptions. Для вложенных агрегатов можно использовать Flatten(). WhenAll дожидается всех переданных задач, а не завершается сразу после первой ошибки.
Документация: Task.WhenAll
Основа вопроса: Список собеседований Alibaba Travels, раздел Task.WhenAll.
➡️ Навигация: База знаний • Задачи • Собеседования • Вакансии
🐸 Библиотека программиста
Чем ExecutionContext отличается от SynchronizationContext?
Ответ: ExecutionContext переносит логическое окружение выполнения, например значения AsyncLocal. SynchronizationContext определяет, как и куда отправить продолжение: например, в очередь потока интерфейса. Это разные механизмы. ConfigureAwait(false) отключает попытку вернуться в захваченный контекст синхронизации, но сам по себе не запрещает перенос ExecutionContext. Поэтому значения AsyncLocal обычно сохраняются и после такого await.
Документация: ExecutionContext и SynchronizationContext
Основа вопроса: Список собеседований Alibaba Travels.
➡️ Навигация: База знаний • Задачи • Собеседования • Вакансии
🐸 Библиотека программиста
Чем скрытие метода через new отличается от override?
Ответ: override заменяет реализацию виртуального метода. При вызове через ссылку базового типа выбирается реализация по фактическому типу объекта. new создаёт отдельный метод, который скрывает одноимённый член. Какой из них доступен при обычном вызове, зависит от типа переменной. Поэтому один объект может вести себя по-разному при обращении через переменные базового и производного типов. new не превращает скрытие в переопределение.
Документация: new и override
Основа вопроса: Материалы собеседований Alibaba Travels, раздел new versus override.
➡️ Навигация: База знаний • Задачи • Собеседования • Вакансии
🐸 Библиотека программиста
Почему для денежных расчётов часто выбирают decimal, а не double?
Ответ: decimal хранит числа в десятичном представлении и точно представляет многие привычные десятичные дроби, например 0.1. double использует двоичное представление: для таких значений обычно приходится хранить приближение. Но decimal не отменяет округление. Например, результат деления единицы на три не представим точно и в этом типе. Для расчётов всё равно нужны правила точности и округления. Выбор типа сам по себе их не задаёт.
Документация: double и decimal
Основа вопроса: Материалы собеседований Alibaba Travels, раздел double versus decimal.
➡️ Навигация: База знаний • Задачи • Собеседования • Вакансии
🐸 Библиотека программиста
Почему Dictionary может перестать находить ключ после изменения его полей?
Ответ: Если изменённые поля участвуют в вычислении хеша или сравнении ключей, объект может перестать соответствовать условиям, с которыми его добавили в словарь. При вставке словарь сохраняет хеш, а при поиске вычисляет его заново. Изменившийся хеш может направить поиск в другое место. Поэтому данные, от которых зависят равенство и хеш ключа, должны оставаться стабильными, пока ключ хранится в словаре. Изменение постороннего поля само по себе такой проблемы не создаёт.
Документация: требования к ключам Dictionary
➡️ Навигация: База знаний • Задачи • Собеседования • Вакансии
🐸 Библиотека программиста
Гарантирует ли IReadOnlyList, что коллекцию и её элементы никто не изменит?
Ответ: Нет. Интерфейс позволяет читать элементы и размер списка, но не обещает, что сам объект неизменяемый. Например, обычный List можно передать как IReadOnlyList и продолжать менять через другую ссылку. Эти изменения будут видны читателю. Кроме того, элементы могут быть изменяемыми объектами. Даже неизменяемая структура коллекции не делает их неизменяемыми автоматически. Если нужен снимок данных, его нужно создавать отдельно.
Документация: IReadOnlyList
➡️ Навигация: База знаний • Задачи • Собеседования • Вакансии
🐸 Библиотека программиста
Зачем структуре IEquatable<T>, если уже есть Equals(object)?
Ответ: Интерфейс задаёт типизированный метод сравнения. Ему не нужно принимать значение как object. Стандартный EqualityComparer<T>.Default использует эту реализацию, что позволяет сравнивать структуры без упаковки ради вызова Equals(object). При этом реализации должны быть согласованы: типизированный Equals, Equals(object) и GetHashCode должны одинаково понимать равенство. Если два значения равны, их хеши тоже должны совпадать.
Документация: IEquatable<T>
➡️ Навигация: База знаний • Задачи • Собеседования • Вакансии
🐸 Библиотека программиста
Повторит ли Lazy<T> создание значения после ошибки в фабрике?
Ответ: Для фабрики с режимом ExecutionAndPublication — нет. Исключение сохраняется, и следующие обращения к Value снова его выбрасывают. Повторного вызова фабрики не будет. В режиме PublicationOnly исключения фабрики не кэшируются: следующее обращение может запустить её заново. Но этот режим также допускает одновременную работу нескольких фабрик. Поэтому поведение зависит от способа создания Lazy, а не только от самого типа.
Документация: ленивая инициализация и кэширование исключений
➡️ Навигация: База знаний • Задачи • Собеседования • Вакансии
🐸 Библиотека программиста
👀 Что копируется при boxing структуры и что происходит при unboxing?
Ответ: При boxing значение структуры копируется в объект в управляемой куче. Изменения исходной переменной после упаковки не меняют упакованную копию.
При обычном приведении из object обратно к структуре значение снова копируется. Тип при распаковке должен совпадать с упакованным: если в object лежит int, выражение (long)value выбросит InvalidCastException. Сначала нужно распаковать int, затем преобразовать его в long.
Поля ссылочного типа копируются вместе со ссылками. Поэтому две копии структуры могут продолжать работать с одним вложенным объектом.
Документация
Вопрос из подборки для собеседований
➡️ Навигация: База знаний • Задачи • Собеседования • Вакансии
🐸 Библиотека программиста
👀 Почему IEnumerable<string> можно передать как IEnumerable<object>, а List<string> как List<object> — нельзя?
Ответ: IEnumerable<T> объявлен с ковариантным параметром out T. Он позволяет получать элементы, поэтому последовательность строк можно использовать там, где ожидают последовательность объектов.
List<T> допускает добавление элементов и остаётся инвариантным. Если бы List<string> можно было использовать как List<object>, через такую ссылку в список строк удалось бы добавить число.
Для чтения выбирайте подходящий интерфейс. Преобразование IEnumerable<string> в IEnumerable<object> не копирует элементы. Эта вариантность работает со ссылочными типами: IEnumerable<int> так преобразовать нельзя.
Документация
Вопрос из подборки для собеседований
➡️ Навигация: База знаний • Задачи • Собеседования • Вакансии
🐸 Библиотека программиста
👀 Когда выбирать ValueTask и можно ли ожидать его несколько раз?
Ответ: ValueTask<T> полезен в часто вызываемых методах, которые нередко завершаются синхронно. Он может хранить готовый результат прямо внутри структуры и экономить выделение Task<T>.
Один экземпляр ValueTask в общем случае нужно ожидать один раз. Он может опираться на переиспользуемый IValueTaskSource, для которого повторный await не поддерживается.
Если результат требуется ожидать несколько раз или передавать в комбинации задач, один раз вызовите AsTask() и сохраните полученную Task. Выбор ValueTask стоит подтверждать измерениями; обычный Task остаётся удобным вариантом для большинства API.
Документация
Вопрос из подборки для собеседований
➡️ Навигация: База знаний • Задачи • Собеседования • Вакансии
🐸 Библиотека программиста