¿Por qué no puedo desempaquetar un int como un decimal?

Tengo un IDataRecord reader que estoy recuperando un decimal de la siguiente manera:

 decimal d = (decimal)reader[0]; 

Por alguna razón, esto arroja una excepción de conversión inválida que dice que “el reparto especificado no es válido”.

Cuando lo hago, el reader[0].GetType() me dice que es un Int32. Hasta donde yo sé, esto no debería ser un problema …

He probado esto 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 está descomprimiendo el int contenido en el lector como un decimal.

¿Alguien sabe por qué esto podría estar ocurriendo? ¿Hay algo sutil que me estoy perdiendo?

Solo puede desvincular un tipo de valor a su tipo original (y la versión que admite valores nulos de ese tipo).

Por cierto, esto es válido (solo una abreviatura para 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 lea esta entrada del blog de Eric Lippert: Representación e identidad

Personalmente, clasifico las cosas hechas por syntax de molde en cuatro tipos diferentes de operación (todas tienen diferentes instrucciones de IL):

  1. Boxeo (instrucción IL de la box ) y unbox (instrucción unbox IL)
  2. Casting a través de la jerarquía de inherencia (como dynamic_cast en C ++, utiliza la instrucción de castclass IL para verificar)
  3. Casting entre tipos primitivos (como static_cast en C ++, hay muchas instrucciones de IL para diferentes tipos de moldes entre tipos primitivos)
  4. Llamar a los operadores de conversión definidos por el usuario (en el nivel IL solo son llamadas al método apropiado op_XXX ).

No hay problema para convertir un int a decimal , pero cuando está desempaquetando un objeto, debe usar el tipo exacto que contiene el objeto.

Para desempaquetar el valor int en un valor decimal , primero lo desempaqueta como un int y luego lo convierte a decimal:

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

La interfaz IDataRecord también tiene métodos para desempaquetar el valor:

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

Aquí hay una solución simple. Se encarga de unboxing y luego de lanzar a decimal. Funcionó bien para mí

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

Mehrdad Afshari lo dijo:

Solo puede desvincular un tipo de valor a su tipo original (y la versión que admite valores nulos de ese tipo).

Lo que hay que darse cuenta es que hay una diferencia entre lanzar y desempaquetar . jerryjvl tuvo un excelente comentario

En cierto sentido, es una pena que el unboxing y el casting sean sintácticamente idénticos, ya que son operaciones muy diferentes.

Fundición:

 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?