2009-07-06 9 views
59

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

decimal d = (decimal)reader[0]; 

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

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

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

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

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

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

ответ

74

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

Кстати, это справедливо (просто сокращение для два версии строки):

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

По той причине, за это прочитать Eric Lippert's blog entry: Representation and Identity

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

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

В некотором смысле стыдно, что распаковка и литье синтаксически выглядят одинаково, поскольку они представляют собой очень разные операции. – jerryjvl

+0

Thanks Mehrdad. Ваше объяснение и ссылка на блог Эрика были весьма полезными. – mezoid

+0

Спасибо! Это бросило меня за петлю. – Darryl

14

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

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

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

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

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

Спасибо за ваш ответ тоже Гуффа ... это было очень полезно. – mezoid

+0

Но если значение иногда является полным числом, но в других случаях истинным десятичным. литье в int сначала потеряет десятичное значение. Если вы ожидаете удвоения, тогда убедитесь, что вы убедитесь, что источник дает вам dobule. В противном случае вы получите побочные эффекты, которые заставят конечного пользователя поцарапать его волосы, а затем совать ваши глазные яблоки. Хороший ответ, но очень плохой совет: он заслуживает -1 – ppumkin

+1

@ppumkin: Извините, если вы неправильно поняли ответ. Он не набрасывается на 'int', а затем на' decimal', это * unboxing * 'int', а затем литье на' decimal'. Unboxing значение как 'int' никогда не заставит его вообще ничего потерять, потому что невозможно удалить его как' int', если на самом деле это не 'int'. – Guffa

3

Mehrdad Афшари сказал это:

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

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

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

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