¿Por qué no puedo desempaquetar un int como decimal?
Pregunta
Yo tengo un IDataRecord reader
del que estoy recuperando un decimal de la siguiente manera:
decimal d = (decimal)reader[0];
Por alguna razón, esto genera una excepción de conversión no válida que dice que "la conversión especificada no es válida".
Cuando lo hago reader[0].GetType()
me dice que es un Int32.Hasta donde yo sé esto no debería ser un problema....
Lo probé con este fragmento que funciona bien.
int i = 3750;
decimal d = (decimal)i;
Esto me ha dejado rascándome la cabeza preguntándome por qué no se puede desempaquetar el int contenido en el lector como decimal.
¿Alguien sabe por qué podría estar ocurriendo esto?¿Hay algo sutil que me estoy perdiendo?
Solución
Solo puede desempaquetar un tipo de valor a su tipo original (y la versión anulable de ese tipo).
Por cierto, esto es válido (sólo una abreviatura de su versión de dos líneas):
object i = 4;
decimal d = (decimal)(int)i; // works even w/o decimal as it's a widening conversion
Por la razón detrás de esto lee esto Entrada del blog de Eric Lippert:Representación e identidad
Personalmente, clasifico las cosas realizadas mediante sintaxis de conversión en cuatro tipos diferentes de operaciones (todas tienen diferentes instrucciones IL):
- boxeo (
box
instrucción IL) y unboxing (unbox
instrucción IL) - Transmitir a través de la jerarquía de herencia (como
dynamic_cast<Type>
en C++, utilizacastclass
Instrucción IL para verificar) - Lanzamiento entre tipos primitivos (como
static_cast<Type>
en C++, hay muchas instrucciones IL para diferentes tipos de conversiones entre tipos primitivos) - Llamar a operadores de conversión definidos por el usuario (en el nivel IL, son solo llamadas a métodos al operador apropiado).
op_XXX
método).
Otros consejos
No hay ningún problema en que emitan un int
a decimal
, pero cuando se está unboxing un objeto que tiene que utilizar el tipo exacto que el objeto contiene.
Para desempacar el valor int
en un valor decimal
, primero unbox como un int, y luego convertirlo a decimal:
decimal d = (decimal)(int)reader[0];
La interfaz IDataRecord también tiene métodos para unboxing el valor:
decimal d = (decimal)reader.GetInt32(0);
Esta es una solución simple. Se ocupa de unboxing y luego fundición a decimal. Trabajado bien para mí.
decimal d = Convert.ToDecimal(reader[0]); // reader[0] is int
Mehrdad Afshari dijo que:
Sólo puede desempacar un tipo de valor a su tipo original (y el anulable versión de ese tipo).
Lo que hay que darse cuenta es que hay una diferencia entre la fundición y unboxing . jerryjvl tuvo una excelente observación
En un sentido es una pena que unboxing y echando un vistazo sintácticamente idénticos, ya que son muy diferentes operaciones.
Con:
int i = 3750; // Declares a normal int
decimal d = (decimal)i; // Casts an int into a decimal > OK
Boxeo / Unboxing:
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?