Почему я не могу распаковать int как десятичное число?

У меня есть IDataRecord reader, что я получаю десятичное число из следующего:

decimal d = (decimal)reader[0];

По какой-то причине это порождает недопустимое исключение броска, говорящее, что "Указанный приведение недействительно".

Когда я делаю reader[0].GetType(), он сообщает мне, что это Int32. Насколько я знаю, это не должно быть проблемой....

Я тестировал этот фрагмент, который отлично работает.

int i = 3750;
decimal d = (decimal)i;

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

Кто-нибудь знает, почему это может произойти? Есть ли что-то тонкое, что мне не хватает?

Ответ 1

Вы можете только удалить тип значения в его исходный тип (и версию с нулевым значением этого типа).

Кстати, это действительно (просто сокращение для вашей двухстрочной версии):

object i = 4;
decimal d = (decimal)(int)i; // works even w/o decimal as it a widening conversion

По этой причине читайте эту запись в блоге Eric Lippert: представление и идентификация

Лично я классифицирую вещи, сделанные синтаксисом cast, на четыре разных типа операции (все они имеют разные инструкции IL):

  • Бокс (box IL-инструкция) и распаковка (unbox IL-команда)
  • Прохождение через иерархию inhertiance (например, dynamic_cast<Type> в С++ использует проверку castclass IL)
  • Листинг между примитивными типами (например, static_cast<Type> в С++, существует множество инструкций IL для разных типов отбрасываний между примитивными типами)
  • Вызов пользовательских операторов преобразования (на уровне IL они просто вызовы методов для соответствующего метода op_XXX).

Ответ 2

Нет проблем при создании int до decimal, но при распаковке объекта вы должны использовать точный тип, который содержит этот объект.

Чтобы удалить значение int в значение decimal, вы сначала распакуете его как int, а затем отбросите его до десятичной:

decimal d = (decimal)(int)reader[0];

Интерфейс IDataRecord также имеет методы для распаковки значения:

decimal d = (decimal)reader.GetInt32(0);

Ответ 3

Вот простое решение. Он заботится о распаковке, а затем опускается до десятичной. Работала хорошо для меня.

decimal d = Convert.ToDecimal(reader[0]);  // reader[0] is int

Ответ 4

Мехрдад Афшари сказал это:

Вы можете только удалить тип значения в его исходный тип (и значение NULL версия этого типа).

Чтобы понять, что существует разница между литьем и распаковкой. jerryjvl имел отличное замечание

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

Кастинг:

int i = 3750; // Declares a normal int
decimal d = (decimal)i; // Casts an int into a decimal > OK

Бокс/распаковка:

object i = 3750; // Boxes an int ("3750" is similar to "(int)3750")
decimal d = (decimal)i; // Unboxes the boxed int into a decimal > KO, can only unbox it into a int or int?