Home » Создание сайтов и программирование » Самый задаваемый на собеседованиях вопрос по программированию на .NET

Самый задаваемый на собеседованиях вопрос по программированию на .NET

Что любят спрашивать на собеседованиях? Вот перечень самых популярных вопросов:

“Почему вы хотите сменить работу?”
“Почему вы хотите работать в нашей компании?”
“Какую зарплату вы хотите получать?”

К сожалению, на эти вопросы нет однозначного ответа. Вы просто должны перед походом на собеседование подготовить наиболее красивый ответ на эти вопросы и надеяться, что он понравится HR менеджеру. Тогда вы попадете на следующий этап собеседования – собеседование с техническим специалистом. И, почти наверняка, он спросит: “Чем отличается структура от класса в .NET?”. Несмотря на очевидную избитость вопроса его очень любят задавать. Вот поэтому я решил разложить этот вопрос по полочкам, чтобы вы, дорогие мои читатели, могли без запинки ответить на этот вопрос.

Для начала хочу уточнить пару моментов.

.NET поддерживает два вида типов: размерный (value) и ссылочный (reference). Переменные размерного типа непосредственно содержат данные, а переменные ссылочного типа содержат ссылку на область памяти, содержащую данные. При этом, память под ссылочные переменные всегда выделяется в куче, а память под размерные, обычно, в стеке. Как известно, в .NET для освобождения неиспользуемой памяти в куче используется сборщик мусора. Особенностью стека является то, что память в нем освобождается автоматически (без каких-либо накладных расходов). Таким образом, уничтожение ссылочных объектов путем сборки мусора менее эффективно, чем размерных.

Другой важный момент – это упаковка (boxing) и распаковка (unboxing). При упаковке размерного типа происходит выделение области памяти в куче и копирование значения размерного типа в эту область. Упакованный размерный тип обладает свойствами ссылочного. Распаковка – обратный процесс, в результате которого упакованный размерный тип копируется на стек. Благодаря упаковке любой размерный тип может интерпретироваться как ссылочный и, как следствие этого, любой размерный тип может использоваться вместо object. Важно понимать, что упаковка и распаковка требуют дополнительных затрат памяти и времени. Поэтому следует избегать этих операций в большом количестве.

Теперь вернемся к нашему вопросу. Отличие структуры от класса в .NET:

  1. Структура является размерным типом, а класс – ссылочным.
  2. Все структурные типы неявно наследуются от System.ValueType, они не бывают абстрактными и всегда неявно запечатаны (sealed)
  3. При присваивании переменных структурного типа, создается копия данных
  4. Объявления полей структуры не могут иметь инициализаторов
  5. Различная интерпретация this для структуры и класса
  6. Структура не может содержать конструктор без параметров
  7. Структура не может содержать деструктор
  8. Для ссылочных типов значение по умолчанию – null
  9. При конвертировании между ссылочным и размерным типами происходит упаковка и распаковка

Не страшно, если вы не сможете вспомнить все различия. Главное помнить и понимать главные:
1 – следствием этого является то, что экземпляр класса создается в куче, а структуры, обычно (но не всегда) на стеке
3 – это должно быть очевидно
4 является следствием 6, т.к. код инициализации полей неявно вставляется во все конструкторы
6 является следствием оптимизации использования структур по скорости
Особенности 8 для структур и 9 рассмотрим ниже более подробно, т.к. именно на них любят акцентировать внимание на собеседовании.

Рассмотрим такой пример:

В чем отличие между объявлениями в строках 7 и 8? Некоторые считают, что в первом случае (строка 7) объект создается на стеке, а во втором (строка 8) происходит упаковка и объект создается в куче. На самом деле это не так. В обоих случаях объект создается на стеке. Разница в том, что в строке 7 будет создан неинициализированный объект, а в строке 8 инициализированный. Поля ms2 будут содержать значения по умолчанию (0 для m1 и null для s1), а поля ms1 неопределены. Поэтому в строке 10 возникнет ошибка компиляции.

Теперь рассмотрим нюансы, связанные с упаковкой и распаковкой. Как известно, размерные типы могут наследоваться от интерфейсов (имплиментировать интерфейсы). Часто спрашивают, будет ли производиться упаковка при приведении размерного типа к интерфейсу. Правильный ответ – да, будет, т.к. интерфейс является ссылочным типом.
Рассмотрим пример:

Размерный тип int имплиментирует интерфейс IFormattable, содержащий метод ToString(). Так как метод ToString() является частью класса int, а компилятор знает, что это размерный тип и, следовательно, виртуальный метод ToString() не может быть переопределен (т.к. структурный тип является запечатанным), компилятор вставляет непосредственный вызов метода в строку 2 и упаковки не происходит. В строке 3 происходит упаковка, т.к. i приводится к интерфейсу IFormattable. Теперь вы сами можете сказать, что происходит в строке 4: неявное приведение к интерфейсу IFormattable и вызов метода ToString(), что, также, приводит к упаковке.

И еще один момент. Массивы являются ссылочными типами, но могут содержать размерные. Где же будет размещен, например, массив целых чисел? В куче, причем целые числа будут неупакованными.

Для более детального изучения этого материала рекомендую книгу Джефри Рихтера.