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

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

decimal d = (decimal)reader[0];

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

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

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

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

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

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

4 ответов


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

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

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

по причине этого прочитайте это запись в блоге Эрика Липперта: представление и идентичность

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

  1. бокс (box инструкция ил) и распаковка (unbox Ил инструкция)
  2. литье по иерархии inhertiance (например,dynamic_cast<Type> В C++, использует castclass Il инструкция для проверки)
  3. преобразование между примитивными типами (например,static_cast<Type> в C++ существует множество инструкций IL для различных типов приведений между примитивными типами)
  4. вызов пользовательских операторов преобразования (на уровне IL они являются просто методом звонки на соответствующие op_XXX метод).

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

распаковывать в int стоимостью в decimal значение, вы сначала распаковываете его как int, а затем бросаете его в decimal:

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

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

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

вот простое решение. Он заботится о распаковке, а затем о приведении к decimal. Отлично сработало.

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

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

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

дело в том, чтобы понять, что существует разница между литьем и распаковкой. 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?