Равенство, null и dynamic
Равенство по значению и по ссылке, Equals/GetHashCode, nullable-типы, неизменяемость, var/dynamic/object и вывод типов.
5 вопросов
MiddleТеорияОчень частоЧто такое Nullable<T> / int?, и как безопасно прочитать его значение?
Что такое Nullable<T> / int?, и как безопасно прочитать его значение?
Nullable<T> (сокращённо int?) — это struct, позволяющий значимому типу также представлять null. Он даёт HasValue для проверки наличия и Value для чтения, что бросает исключение, если значения нет. Безопасное чтение использует GetValueOrDefault() или оператор ??.
Типичные ошибки
- ✗Читать
.Valueбез проверкиHasValue, что бросаетInvalidOperationException, когда значения нет - ✗Думать, что
int?— ссылочный тип, а неstruct, оборачивающий значимый тип - ✗Полагать, что
Nullable<T>работает для ссылочных типов, хотяTдолжен быть non-nullable значимым типом
Уточняющие вопросы
- →Что возвращает
GetValueOrDefault(), когда у nullable нет значения? - →Как подъём операторов даёт
nullпри арифметике над двумя операндамиint??
JuniorТеорияЧастоЧто делает var, и является ли объявленная им переменная динамически типизированной?
Что делает var, и является ли объявленная им переменная динамически типизированной?
var указывает компилятору вывести СТАТИЧЕСКИЙ тип переменной из инициализатора при компиляции. Это в точности эквивалентно явному типу — var x = 5; это int x = 5;. Переменная статически типизирована и не сменит тип позже; var — не dynamic.
Типичные ошибки
- ✗Путать
varсdynamicи считать, что он обходит проверку типов на этапе компиляции - ✗Считать, что
var-переменную можно переприсвоить значением другого типа после объявления - ✗Думать, что
varимеет накладные расходы во время выполнения по сравнению с явным типом
Уточняющие вопросы
- →Почему объявление с
varвсегда должно иметь инициализатор в той же строке? - →Когда
varобязателен, а не опционален в коде на C#?
MiddleТеорияЧастоЧем == и Equals() различаются для классов и значимых типов?
Чем == и Equals() различаются для классов и значимых типов?
Для class и ==, и Equals() по умолчанию дают ссылочное равенство; значимые типы сравнивают по значению. == — статический перегружаемый оператор, разрешаемый при компиляции, а Equals — переопределяемый виртуальный метод. string переопределяет оба для сравнения по значению.
Типичные ошибки
- ✗Ожидать, что
==для двух разных экземпляров класса с равными полями вернёт true по умолчанию - ✗Думать, что
==виртуален и диспетчеризуется какEquals, хотя это статически разрешаемый оператор - ✗Полагать, что
string ==сравнивает ссылки, а не последовательность символов по значению
Уточняющие вопросы
- →Почему переопределение
Equalsдолжно сопровождаться переопределениемGetHashCode? - →Как
==иEqualsмогут дать разные результаты на одной паре операндов?
MiddleТеорияИногдаЧто значит, что string неизменяем, и почему это важно?
Что значит, что string неизменяем, и почему это важно?
Неизменяемость означает, что состояние объекта не меняется после создания. Так как string неизменяем, любое изменение вроде Replace или ToUpper выделяет новый string, оставляя исходный нетронутым. Для активных правок в цикле берут StringBuilder, избегая лишних экземпляров.
Типичные ошибки
- ✗Считать, что метод вроде
Replaceменяет исходную строку, а не возвращает новыйstring - ✗Конкатенировать в цикле через
+, создавая много короткоживущих строк вместоStringBuilder - ✗Путать неизменяемость самого объекта-строки с тем, можно ли переприсвоить переменную, хранящую его
Уточняющие вопросы
- →Как интернирование строк использует неизменяемость для разделения одинаковых литералов?
- →Почему неизменяемый тип по своей природе безопасно читать из нескольких потоков без блокировки?
MiddleТеорияИногдаЧем var, dynamic и object отличаются по моменту разрешения типов?
Чем var, dynamic и object отличаются по моменту разрешения типов?
var — вывод на этапе компиляции: переменная получает конкретный статический тип с проверкой. object — статический базовый тип всего, поэтому для конкретных вызовов нужно приведение. dynamic откладывает связывание членов на выполнение; неверный член падает при выполнении.
Типичные ошибки
- ✗Путать
var(статический тип на этапе компиляции) сdynamic(связывание во время выполнения) - ✗Думать, что
objectпропускает проверку членов при компиляции, хотя он требует явного приведения - ✗Полагать, что ошибки
dynamicловятся при компиляции, а не бросаются во время выполнения
Уточняющие вопросы
- →Какую инфраструктуру времени выполнения использует
dynamicдля разрешения вызовов членов? - →Почему вызов метода у переменной
objectтребует сначала приведения?