Смущает бокс. Приведение -1 к Int64 вызывает InvalidCastException

Хорошо, я, должно быть, упускаю из виду что-то очень простое, но я потерян.

Учитывая это

object val = -1;
var foo = (Int32)(val);
var bar = (Int64)(val);

Приведение к броскам Int64 и InvalidCastException.

Я понимаю, что это связано с некоторой странностью бокса, но я не понимаю причин.

Насколько я понимаю, val в первой строке упакован как Int32.

Затем, когда я пытаюсь использовать что-то другое, кроме Int32 InvalidCastException. Я полагаю, это означает, что я пытаюсь распаковать val как Int64, хотя на самом деле это Int32?

Все равно кажется странным. Разве приведение не могло распаковать значение, а затем попытаться выполнить приведение?

Что-то вроде (очевидно, это ужасно упрощено, может быть, тип в штучной упаковке неизвестен, поэтому это невозможно?):

object val = -1;
Int32 unboxed = (Int32)(val);
var bar = (Int64)(unboxed);

Кто-то (читай: Эрик Липперт) объяснит мне, что стоит за этим.

ОБНОВЛЕНИЕ: из блога Эрика, на который Рид разместил ссылку, это краткий ответ, который я искал

"...Это будет огромный объем кода для генерации, и он будет очень медленным. Код, конечно, настолько велик, что вы захотите поместить его в собственный метод и просто сгенерировать вызов к нему. . Вместо того, чтобы делать это по умолчанию и всегда генерировать медленный, большой и хрупкий код, вместо этого мы решили, что распаковка может распаковывать только до точного типа. Если вы хотите вызвать медленный метод который делает все это, он доступен — вы всегда можете вызвать Convert.ToInt32, который сделает весь этот анализ во время выполнения для вас Мы даем вам выбор между «быстро и точно» или «медленно и небрежно», и разумным значением по умолчанию является первое. Если вы хотите второе, вызовите метод...."


person Brad Cunningham    schedule 13.10.2010    source источник
comment
Это что-то крайне непростое. Многих это сбивает с толку...   -  person Reed Copsey    schedule 13.10.2010
comment
Рид прав; об этом спрашивают несколько раз в неделю на SO. Вчера дважды спрашивали.   -  person Eric Lippert    schedule 14.10.2010
comment
Итак, я явно хромой, потому что задаю тот же вопрос, что и все остальные, и не ищу сначала. Мне кажется интересным, что это все еще получает 6 голосов, если это такой распространенный вопрос. По-видимому, поведение по умолчанию не самое очевидное поведение.   -  person Brad Cunningham    schedule 16.10.2010


Ответы (1)


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

Из-за этого требуется, чтобы объект был распакован, а затем преобразован в Int64:

object val = -1;
int foo = (Int32)val;
Int64 bar = (Int64)(Int32)val;

Эрик Липперт подробно рассказал об этом в своем блоге под названием Репрезентация и личность. .

person Reed Copsey    schedule 13.10.2010
comment
Круто, Эрик Липперт по доверенности :) Ты молодец - person Brad Cunningham; 13.10.2010
comment
Другой способ написать: Int64 a = -1; object o = a; Int64 b = (Int64)o; Спасибо, Рид! - person Nayan; 13.10.2010
comment
Не могли бы люди перестать говорить приведение, когда они имеют в виду конверсию? Между целочисленными типами нет «приведения»; они конвертируются. (К сожалению, C# использует один и тот же синтаксис для обоих.) - person Timwi; 13.10.2010
comment
@Timwi: Я согласен с вами, но вы ведете тяжелую битву. Я предпочитаю, чтобы резервное приведение имело в виду фактический текст оператора приведения во время компиляции в коде и преобразование для операции преобразования одного типа в другой во время выполнения. Но большинство людей не делают этого различия. - person Eric Lippert; 14.10.2010