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

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

decimal d = (decimal)reader[0];

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

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

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

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

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

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


person mezoid    schedule 06.07.2009    source источник


Ответы (4)


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

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

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

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

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

  1. Бокс (box инструкция IL) и распаковка (unbox инструкция IL)
  2. Приведение через иерархию наследования (например, dynamic_cast<Type> в C ++, для проверки используется инструкция castclass IL)
  3. Преобразование между примитивными типами (например, static_cast<Type> в C ++, существует множество инструкций IL для разных типов преобразований между примитивными типами)
  4. Вызов определенных пользователем операторов преобразования (на уровне IL они просто вызовы метода к соответствующему op_XXX методу).
person mmx    schedule 06.07.2009
comment
В некотором смысле жаль, что распаковка и приведение синтаксически выглядят одинаково, поскольку это очень разные операции. - person jerryjvl; 06.07.2009
comment
Спасибо, Мердад. Ваше объяснение и ссылка на блог Эрика были весьма полезны. - person mezoid; 06.07.2009
comment
Спасибо! Это поставило меня в тупик. - person Darryl; 24.09.2009
comment
Но если значение иногда является полным числом, а иногда - истинным десятичным числом. приведение к int сначала потеряет десятичное значение. Если вы ожидаете двойного, то УБЕДИТЕСЬ, проверив, что источник дает вам добул. В противном случае вы получите побочные эффекты, которые заставят конечного пользователя почесать волосы, а затем ткнуть вам в глаза. Хороший ответ, но очень плохой совет - заслуживает -1. - person Piotr Kula; 15.10.2012
comment
@ppumkin: Вы, кажется, упускаете самое главное. Вы не сможете использовать object a = (decimal)5; напрямую с (int)a. Это выбросит InvalidCastException. - person mmx; 15.10.2012
comment
Что, если значение равно 5.00, тогда (decimal)5.00 действительно, да? Можете ли вы гарантировать, что это не будет 5.00? Если вы можете, тогда нет необходимости переводить его в десятичный формат. Разве я действительно что-то здесь не понимаю? - person Piotr Kula; 16.10.2012
comment
@ppumkin Пример не о необходимости приведения к decimal или любому другому типу в этом отношении. Речь идет о возможности преобразования упакованного значения непосредственно в другой тип, демонстрируя тонкую разницу между преобразованием упаковки / распаковки и простым преобразованием примитивного типа, несмотря на то, что все они используют один и тот же синтаксис преобразования. - person mmx; 16.10.2012
comment
Ладно - соглашусь. У меня была проблема, когда я хотел всегда иметь двойной, даже если он был помещен в коробку int. Я погуглил и получил ответ. Мне просто нужно убедиться, что он упакован как двойной, чтобы избежать этой проблемы. Спасибо за объяснение +1 - person Piotr Kula; 18.10.2012

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

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

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

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

decimal d = (decimal)reader.GetInt32(0);
person Guffa    schedule 06.07.2009
comment
Спасибо и за ответ, Гуффа ... это было очень полезно. - person mezoid; 06.07.2009
comment
Но если значение иногда является полным числом, а иногда - истинным десятичным числом. приведение к int сначала потеряет десятичное значение. Если вы ожидаете двойного, то УБЕДИТЕСЬ, проверив, что источник дает вам добул. В противном случае вы получите побочные эффекты, которые заставят конечного пользователя почесать волосы, а затем ткнуть вам в глаза. Хороший ответ, но очень плохой совет - заслуживает -1. - person Piotr Kula; 15.10.2012
comment
@ppumkin: Извините, если вы неправильно поняли ответ. Это не преобразование в int, а затем в decimal, это распаковка int, а затем преобразование в decimal. Распаковка значения как int никогда не заставит его вообще ничего потерять, потому что невозможно распаковать его как int, если на самом деле это не int для начала. - person Guffa; 15.10.2012

Вот простое решение. Он позаботится об распаковке и последующем преобразовании в десятичное число. У меня сработало нормально.

decimal d = Convert.ToDecimal(reader[0]);  // reader[0] is int
person Sagar    schedule 28.12.2015
comment
Это был ответ для меня - мои возвращаемые типы, казалось, динамически определялись размером возвращаемого числа. Это справилось с преобразованием упакованного целого числа или десятичного числа в рамке в распакованное десятичное число. - person Chris; 08.04.2018

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

Вы можете распаковать только тип значения в его исходный тип (и версию этого типа, допускающую значение 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?
person user276648    schedule 04.10.2011