Какие ошибки не видит компилятор, но видит run-time?

379
07 марта 2017, 13:09

Тест на собеседовании

Проходил онлайн-собеседование и был предложен тест:

What is the result of the following code?

int a = 5;
object b = a;
double c = (double)b;
Console.Write(c);
  • 5
  • compilation error
  • Neither answer is correct
  • run-time error (exception)

Мой ответ

На сколько я понял это тест на глубокое понимание внутренней "кухни" C#.

И дал я на него не правильный ответ - 5.

После всего я набрал данный пример и вот что получилось во время выполнения:

Вопрос

  1. Компилятор ничего не подсветил. Почему так произошло? Что не может заранее увидеть компилятор, что может выявится во время run-time?
  2. Правильно ли я понял, что в строке object b = a; переменная b динамически получает ссылочный тип, а на строке double c = (double)b; мы пытаемся преобразовать ссылочный тип в значимый ((double)b) и поэтому выскакивает исключение?
Answer 1

Никакой особой «внутренней кухни» тут нет.

Для начала, выражение object b = a;. Это упаковка. Согласно документации, упаковка всегда возможна и является неявным преобразованием. Поэтому компилятор не ругается на несоответствие типов, упаковка в object возможна всегда.

Теперь, выражение double c = (double)b;. Тип bobject, поэтому это выражение является распаковкой в значимый тип double. Смысл распаковки такой: если в b в самом деле упаковано значение в точности типа double, то оно оттуда берётся, иначе происходит исключение.

Компилятор не следит за тем, какой тип был ранее упакован в b (потому что в общем случае он не может этого сделать), так что он не знает, что в b реально int. Чего компилятор не делает, так это попытки достать int из b, и попытаться преобразовать его к double. Опять-таки потому, что он не умеет преобразовать во время выполнения произвольный значимый тип в другой значимый тип (а точный тип, лежащий в b, компилятор не знает).

В вашем случае в b оказывается упакованный int, так что происходит «законное» исключение.

Answer 2

Boxing/Unboxing

int a = 5;
object b = a;

В CLR существует такое понятие как boxing — это преобразование value-of типа в ссылочный тип. Когда вы выполняете неявное преобразование в System.Object в этом примере, CLR создаст объект в куче и присвоит его полю значение 5. Этот механизм существует по ряду причин, одна из которых, это утверждение что любой тип в CLR наследуется от System.Object

double c = (double) b;

Обратный процесс называется unboxing и одной из его особенностей является то, что упакованный тип должен быть распакован именно в тот тип, в который был упакован. В вашем примере, запакованный тип — Int32, значит именно в Int32 он и должен быть распакован.

Если выполнить ваш пример и пропустить этап boxing/unboxing то всё было бы корректно:

int a = 5;
double c = (double) a;

Если выполнить требования CLR и распаковать тип в нужный, то дальнейшее преобразование тривиально:

int a = 5;
object b = a;
double c = (int) b;
READ ALSO
Как избежать ошибки PathTooLongException?

Как избежать ошибки PathTooLongException?

Имеется программа обрабатывающая различные файловые пути (напр: C:\Users\J

380
Определить язык произвольной строки

Определить язык произвольной строки

Из вне приходит произвольная строка и нужно определить ее язык

438
Добавление выбранной пользователем ватермарки на выбранную им же картинку?

Добавление выбранной пользователем ватермарки на выбранную им же картинку?

Суть проста В picturebox загружается картинка, потом по нажатию кнопки добавляется ватермарка НО! Сейчас добавляется только указанная в пути...

298
Определение типа значения в строке из XML

Определение типа значения в строке из XML

Имеется XML документ, из него идет парсинг значений полей и далее необходимо узнать тип значения записанный в строковой переменнойДля определения...

343