Почему я не могу распаковать значение int как десятичное?
Вопрос
У меня есть IDataRecord reader
из которого я извлекаю десятичное число следующим образом:
decimal d = (decimal)reader[0];
По какой-то причине это выдает недопустимое исключение приведения, в котором говорится, что "Указанное приведение недопустимо".
Когда я делаю reader[0].GetType()
это говорит мне, что это Int32.Насколько я знаю, это не должно быть проблемой....
Я проверил это с помощью этого фрагмента, который работает просто отлично.
int i = 3750;
decimal d = (decimal)i;
Это заставило меня почесать в затылке, задаваясь вопросом, почему не удается распаковать значение int, содержащееся в reader, в виде десятичного числа.
Кто-нибудь знает, почему это может происходить?Есть ли что-то тонкое, чего мне не хватает?
Решение
Вы можете только распаковать тип значения до его исходного типа (и обнуляемую версию этого типа).
Кстати, это действительно (просто сокращение для вашей двухстрочной версии):
object i = 4;
decimal d = (decimal)(int)i; // works even w/o decimal as it's a widening conversion
О причине, стоящей за этим, прочтите это Запись в блоге Эрика Липперта:Представительство и Идентичность
Лично я классифицирую действия, выполняемые с помощью синтаксиса приведения, на четыре различных типа операций (все они имеют разные инструкции IL).:
- Бокс (
box
Инструкция IL) и распаковка коробки (unbox
IL instruction) - Приведение через иерархию наследования (например
dynamic_cast<Type>
в C ++ используетсяcastclass
(инструкция для проверки) - Приведение между примитивными типами (например
static_cast<Type>
в C ++ существует множество инструкций IL для различных типов приведения между примитивными типами) - Вызов определяемых пользователем операторов преобразования (на уровне IL это просто вызовы методов к соответствующему
op_XXX
способ).
Другие советы
Нет никаких проблем в том, чтобы разыграть int
Для decimal
, но когда вы распаковываете объект, вы должны использовать точный тип, который содержит объект.
Чтобы распаковать int
значение в decimal
значение, вы сначала распаковываете его как int, затем приводите к десятичному:
decimal d = (decimal)(int)reader[0];
Интерфейс IDataRecord также имеет методы для распаковки значения:
decimal d = (decimal)reader.GetInt32(0);
Вот простое решение.Он заботится о распаковке и последующем преобразовании в десятичное число.У меня это прекрасно сработало.
decimal d = Convert.ToDecimal(reader[0]); // reader[0] is int
Это сказал Мехрдад Афшари:
Вы можете только разархивировать тип значения до его исходного типа (и nullable версия этого типа).
Главное, что нужно осознать , это то , что есть разница между кастингом и распаковкой коробки.у джерриджвла было отличное замечание
В некотором смысле обидно, что синтаксически распаковка и приведение выглядят идентично, поскольку это очень разные операции.
Кастинг:
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?