Странное литье.Невозможно привести объект (int) к длинному - PullRequest
42 голосов
/ 22 августа 2010

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

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?

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

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

Есть мысли?

Ответы [ 5 ]

50 голосов
/ 22 августа 2010

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

Приведение с object к int интерпретируется как распаковка в штучной упаковке int.

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

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

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

7 голосов
/ 22 августа 2010

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

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

long longNumber2 = (long) (int) intNumber2;

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

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

3 голосов
/ 22 августа 2010

(Внимание: угадайте)

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

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

1 голос
/ 22 августа 2010

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

0 голосов
/ 22 августа 2010

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

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

long result = (long)intNumber2;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...