Страницы

Поиск по вопросам

воскресенье, 8 декабря 2019 г.

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

#c_sharp #исключения #runtime


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

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


  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.

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



Вопрос


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

    


Ответы

Ответ 1



Никакой особой «внутренней кухни» тут нет. Для начала, выражение object b = a;. Это упаковка. Согласно документации, упаковка всегда возможна и является неявным преобразованием. Поэтому компилятор не ругается на несоответствие типов, упаковка в object возможна всегда. Теперь, выражение double c = (double)b;. Тип b — object, поэтому это выражение является распаковкой в значимый тип double. Смысл распаковки такой: если в b в самом деле упаковано значение в точности типа double, то оно оттуда берётся, иначе происходит исключение. Компилятор не следит за тем, какой тип был ранее упакован в b (потому что в общем случае он не может этого сделать), так что он не знает, что в b реально int. Чего компилятор не делает, так это попытки достать int из b, и попытаться преобразовать его к double. Опять-таки потому, что он не умеет преобразовать во время выполнения произвольный значимый тип в другой значимый тип (а точный тип, лежащий в b, компилятор не знает). В вашем случае в b оказывается упакованный int, так что происходит «законное» исключение.

Ответ 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;

Комментариев нет:

Отправить комментарий