Странное поведение при приведении Невозможно преобразовать объект (int) в long

У меня есть следующий код:

int intNumber1 = 100;
object intNumber2 = 100;
bool areNumberOfTheSameType = intNumber1.GetType() == intNumber2.GetType(); // TRUE
bool areEqual = intNumber1.Equals(intNumber2); // TRUE

long longNumber1 = (long) intNumber1; // OK
long longNumber2 = (long) intNumber2; // InvalidCastException. Why?

Почему не работает второе приведение? Я понимаю, что это может быть потому, что объект не имеет явного приведения к длинному , но если мы посмотрим на его тип во время выполнения, это будет System.Int32 .

Если я использую var или dynamic вместо object , это работает.

Есть мысли?

44
задан Nate Barbettini 17 December 2015 в 01:50
поделиться

5 ответов

Приведение из int в long интерпретируется как преобразование между двумя типами.

Приведение из объекта в int интерпретируется как распаковка упакованного int .

Это тот же синтаксис, но он говорит о двух разных вещах.

В рабочих случаях ( int long , объект (в рамке int ) → int ) , компилятор точно знает, какой код производить. Если бы упакованный int long работал, компилятор должен был бы каким-то образом выяснить, какое преобразование использовать, но у него недостаточно информации для этого.

См. Также это сообщение в блоге Эрика Липперта .

53
ответ дан 26 November 2019 в 22:13
поделиться

(Внимание: догадаться)

Int32 имеет оператор преобразования в Int64 , который вызывается при первом приведении. Объект - нет, поэтому ваше второе приведение пытается привести объект к другому типу, который не является супертипом ( Int64 не наследует Int32 ) .

Причина, по которой он работает с var , очевидна - компилятор просто избавляет вас от ввода int в этом случае. С динамическим среда выполнения выполняет все необходимые проверки того, что нужно сделать, в то время как обычно компилятор просто вставляет либо приведение, либо вызывает оператор преобразования.

3
ответ дан 26 November 2019 в 22:13
поделиться

То, что это не работает из-за того, что это два разных типа приведения (одно преобразование, другое распаковка), уже было указано в ответах здесь. Что может быть полезным дополнением, так это то, что Convert.ToInt64 () преобразует все, что является либо встроенным типом, который можно преобразовать в long, либо типом класса, реализующего IConvertible .ToInt64 () в длинный. Другими словами, если вы хотите иметь возможность преобразовать объект, содержащий целое число (любого размера) в long, Convert.ToInt64 () - это то, что вам нужно. Это дороже, но то, что вы пытаетесь сделать , на дороже, чем литье, и разница незначительна (достаточно большая, чтобы быть расточительной в тех случаях, когда вы знаете, что объект должен быть длинным в коробке).

1
ответ дан 26 November 2019 в 22:13
поделиться

Вам нужно распаковать до того же типа, который был упакован в коробку.

object intNumber2 = 100L;
// or value in the long type range
// object intNumber2 = 9223372036854775806;

long result = (long)intNumber2;
0
ответ дан 26 November 2019 в 22:13
поделиться

объект имеет тип int. Но он считается объектом (упакованным целым числом), а упакованный тип значения обычно может быть приведен только к его базовому типу (упакованному типу).

Чтобы привести его к другому типу, сначала нужно привести его к базовому типу. Это работает:

long longNumber2 = (long) (int) intNumber2;

Причина, по которой var работает, заключается в том, что компилятор определяет тип во время компиляции. Это означает, что когда вы используете var, тип intNumber2 (если вы используете typeof) будет int. В то время как при использовании объект тип будет объект.

Использование dynamic — это совершенно другой процесс, и его нельзя сравнивать с var. Здесь преобразование/приведение происходит во время выполнения с использованием отражения и библиотеки DLR. Он динамически найдет базовый тип, обнаружит, что у него есть оператор преобразования, и использует его.

7
ответ дан 26 November 2019 в 22:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: