Система типов
Значимые и ссылочные типы, семантика struct, упаковка/распаковка и проверка типов во время выполнения.
6 вопросов
JuniorТеорияОчень частоЧем значимые и ссылочные типы отличаются по хранению и присваиванию?
Чем значимые и ссылочные типы отличаются по хранению и присваиванию?
Значимый тип (struct, int) хранит данные на месте — на стеке или в контейнере — и присваивание копирует всё значение. Ссылочный тип (class, string) живёт в куче, а переменная держит ссылку, поэтому присваивание копирует ссылку, и обе её разделяют.
Типичные ошибки
- ✗Считать, что все объекты живут в куче — локальные значимые переменные хранятся на месте, часто на стеке
- ✗Думать, что присваивание ссылочного типа копирует объект, а не только ссылку
- ✗Полагать, что
string— значимый тип из-за неизменяемого поведения, хотя это ссылочный тип
Уточняющие вопросы
- →Где хранится значимый тип, когда он является полем класса, размещённого в куче?
- →Что позволяет изменить метод, которому ссылочный тип передан по значению?
MiddleТеорияОчень частоЧто такое упаковка и распаковка, и чего они стоят во время выполнения?
Что такое упаковка и распаковка, и чего они стоят во время выполнения?
Упаковка оборачивает значимый тип в object в куче, неявно при присваивании к object или интерфейсу. Распаковка — явное обратное приведение с копированием данных. Каждая упаковка стоит выделения в куче и копии, поэтому активное использование нагружает сборщик мусора.
Типичные ошибки
- ✗Думать, что упаковка бесплатна, игнорируя выделение в куче и нагрузку на сборку мусора в горячих циклах
- ✗Считать, что распаковка возвращает ссылку в упаковку, а не свежую копию значения
- ✗Не осознавать, что присваивание значимого типа к
objectили интерфейсу упаковывает его неявно
Уточняющие вопросы
- →Почему распаковка приведением к неверному типу выбрасывает
InvalidCastException? - →Как обобщения позволяют коллекции избежать упаковки своих значимых элементов?
JuniorТеорияЧастоЧем отличаются операторы is и as при проверке типа во время выполнения?
Чем отличаются операторы is и as при проверке типа во время выполнения?
is проверяет совместимость с типом, возвращая bool; паттерн if (o is T t) ещё и связывает приведение с t. as пытается привести и возвращает преобразованную ссылку либо null при неудаче, а не бросает исключение. Работает только со ссылочными и nullable-типами.
Типичные ошибки
- ✗Ожидать, что
asвыбросит исключение при неудачном приведении, а не вернётnull - ✗Применять
asк non-nullable значимому типу, что является ошибкой компиляции - ✗Делать проверку
is, а затем отдельное приведение вместо совмещённого паттернаis T t
Уточняющие вопросы
- →Почему результат
asнужно проверять наnullперед использованием? - →Как паттерн
o is T tизбегает второй операции приведения?
MiddleТеорияЧастоВ чём ключевые отличия между struct и class в C#?
В чём ключевые отличия между struct и class в C#?
struct — значимый тип: копируется при присваивании, хранится на месте, не null (если не Nullable) и не наследуется. class — ссылочный тип: экземпляры живут в куче, переменные хранят ссылки, поддерживают наследование и null, а присваивание разделяет объект.
Типичные ошибки
- ✗Считать, что
structподдерживает наследование от другогоstructилиclass - ✗Полагать, что
structвсегда на стеке, забывая, что как поле класса он живёт в куче - ✗Забывать, что передача
structв метод копирует его, поэтому метод не меняет копию вызывающего
Уточняющие вопросы
- →Почему
structможет реализовать интерфейс, хотя не может наследовать базовый тип? - →Когда передача большого
structпо значению становится проблемой производительности?
SeniorТеорияИногдаКак checked и unchecked управляют поведением при переполнении целых?
Как checked и unchecked управляют поведением при переполнении целых?
checked и unchecked задают, обнаруживается ли переполнение целых. В checked операция за диапазоном бросает OverflowException. В unchecked — умолчании для непостоянной арифметики — результат молча оборачивается по модулю размера типа. Оба управляют только целыми типами.
Типичные ошибки
- ✗Менять их местами — думать, что
checkedоборачивает, аuncheckedбросает - ✗Полагать, что
checked/uncheckedвлияют на переполнение плавающей точки, хотя они управляют только целыми типами - ✗Считать, что арифметика проверяется на переполнение по умолчанию, хотя умолчание —
unchecked
Уточняющие вопросы
- →Почему константные выражения проверяются на переполнение при компиляции независимо от контекста?
- →Как настройка переполнения уровня проекта взаимодействует с явными блоками
checked?
SeniorТеорияИногдаЧем различаются typeof(T), obj.GetType() и sizeof?
Чем различаются typeof(T), obj.GetType() и sizeof?
typeof(T) разрешается при компиляции из статического типа T в его Type. obj.GetType() выполняется во время выполнения и возвращает фактический тип экземпляра, возможно производный. sizeof даёт размер в байтах unmanaged значимого типа при компиляции.
Типичные ошибки
- ✗Ожидать, что
typeof(базовыйТип)отразит производный тип экземпляра во время выполнения - ✗Думать, что
GetType()возвращает объявленный статический тип, а не фактический тип времени выполнения - ✗Пробовать
sizeofна managed ссылочном типе, хотя он применим только к unmanaged значимым типам
Уточняющие вопросы
- →Почему
GetType()нельзя вызвать наnull-ссылке? - →Как сопоставление с паттерном через
isсоотносится со сравнением результатовGetType()?