2009-07-06 20 views
59

Tengo una IDataRecord reader que estoy recuperando un decimal a partir de la siguiente manera:¿Por qué no puedo unbox un int como un decimal?

decimal d = (decimal)reader[0]; 

Por alguna razón esto se produce una excepción reparto no válido decir que la "conversión especificada no es válida."

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

He probado esto con este fragmento que funciona muy bien.

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

Esto me ha dejado rascándose la cabeza preguntándose por qué está fallando a desempacar INT contenido en el lector como un decimal.

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

Respuesta

74

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

Por cierto, esto es válido (sólo 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 este Eric Lippert's blog entry: Representation and Identity

Personalmente, categorizar las cosas por la sintaxis fundido en cuatro diferentes tipos de funcionamiento (todos ellos tienen diferentes instrucciones IL):

  1. boxeo (box instrucciones IL) y unboxing (unbox instrucción IL)
  2. de reparto a través de la jerarquía inhertiance (como dynamic_cast<Type> en C++, utiliza castclass instrucción IL para verificar)
  3. conversión entre tipos de primitivas (como static_cast<Type> en C++, hay un montón de instrucciones 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).
+20

En cierto sentido, es una pena que el unboxing y la fundición sean sintácticamente idénticos, ya que son operaciones muy diferentes. – jerryjvl

+0

Gracias Mehrdad. Su explicación y enlace al blog de Eric fue bastante útil. – mezoid

+0

¡Gracias! Esto me lanzó a un bucle. – Darryl

14

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 unbox 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 del valor:

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

Gracias por su respuesta también Guffa ... fue muy útil. – mezoid

+0

Pero si el valor es a veces un número completo pero otras veces un decimal verdadero. lanzando a int primero perderá el valor decimal. Si esperas un doble, ASEGÚRATE por validación de que la fuente te da un dobule. De lo contrario, obtendrás efectos secundarios que harán que el usuario final se rasque el cabello y luego te toque los ojos. Buena respuesta pero muy mala asesoría- Se merece un -1 – ppumkin

+1

@ppumkin: lo siento si no entendiste la respuesta. No está lanzando a 'int' y luego a' decimal', es * unboxing * an 'int' y luego se convierte a' decimal'. Desagrupar el valor como 'int' nunca hará que pierda nada, porque no es posible desempaquetarlo como' int' si en realidad no es un 'int' para empezar. – Guffa

3

Mehrdad Afshari dijo que:

sólo se puede desempacar un valor ty pe a su tipo original (y la versión nulable 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 emitan sintácticamente mirar idéntica, 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? 
12

Aquí es 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 
Cuestiones relacionadas