Чем catch с when отличается от проверки if внутри catch?
Ответ: Фильтр when определяет, подходит ли обработчик, ещё до раскрутки стека. Условие if выполняется уже после входа в catch.
В конструкции catch (Exception ex) when (CanHandle(ex)) обработчик будет выбран только при истинном результате фильтра. Если фильтр вернул false, поиск подходящего catch продолжается. В том числе может сработать следующий catch у того же try.
Если же проверять условие внутри catch, исключение уже перехвачено. Чтобы передать его дальше, придётся явно выполнить throw; — к следующим catch того же try выполнение уже не перейдёт.
При этом throw; сохраняет исходный стек исключения. Его не стоит заменять на throw ex;, который меняет информацию о стеке.
Распространяется ли checked на арифметику внутри вызванного метода?
Ответ: Нет. Контекст checked действует на операции, записанные внутри него, и не переносится через вызов в тело другого метода.
Допустим, метод AddOne(int x) возвращает x + 1. Если обернуть вызов AddOne(int.MaxValue) в checked, это само по себе не заставит сложение внутри AddOne выбросить OverflowException.
Поведение сложения определяется контекстом в самом методе и настройками проверки переполнения при его компиляции. Чтобы явно проверять эту операцию, нужно написать checked(x + 1) в AddOne.
Поэтому без знания настроек компиляции нельзя уверенно обещать конкретный результат исходного примера. Важный принцип: checked не включает проверку во всём стеке вызовов.
Ответ: Нет. Постфиксный ! подавляет предупреждения nullable-анализа для выражения, но ничего не проверяет во время выполнения.
Если переменная value содержит null, обращение value!.Length всё равно завершится NullReferenceException. Оператор не создаёт объект, не подставляет значение и не добавляет проверку.
Использовать ! стоит, когда у разработчика есть основание считать значение ненулевым, а компилятор этого не видит. Если значение действительно может отсутствовать, нужна обработка этого случая: проверка null, условный доступ ?. или подходящее исключение.
Не путайте постфиксный value! с логическим отрицанием !flag. Подавление nullable-предупреждений доступно с C# 8.
Гарантирует ли required, что свойство не будет равно null?
Ответ: Нет. required требует инициализации члена при создании объекта, но не проверяет его значение во время выполнения.
Для свойства required string Name { get; init; } выражение new User() вызовет ошибку компиляции. А new User { Name = null } удовлетворит требованию required. При включённом анализе nullable компилятор выдаст предупреждение о присваивании null; настройки проекта могут превращать такие предупреждения в ошибки.
Атрибут SetsRequiredMembers тоже не доказывает, что конструктор всё заполнил: компилятор доверяет этому обещанию автора.
Поэтому required не заменяет проверку аргументов и правил предметной области. Модификатор доступен с C# 11.
Чем init отличается от set и делает ли он объект неизменяемым?
Ответ: Нет, init сам по себе не делает весь объект неизменяемым.
Свойству с set можно присваивать значение после создания объекта, если позволяет доступ. init ограничивает присваивание этапом инициализации: например, значение можно задать в инициализаторе объекта, а обычное присваивание после этого компилятор запретит.
Но если свойство хранит List<string>, вызов Add по-прежнему допустим. init запрещает заменить ссылку на список после инициализации, а не менять содержимое списка.
На собеседовании стоит разделять две вещи: возможность присвоить свойству новое значение и возможность изменить объект, на который оно ссылается. init появился в C# 9.