web-dev-qa-db-fra.com

Pourquoi ne puis-je pas décompresser un entier en décimal?

J'ai un IDataRecord reader Dont je récupère une décimale comme suit:

decimal d = (decimal)reader[0];

Pour une raison quelconque, cela déclenche une exception de transtypage non valide indiquant que le "transtypage spécifié n'est pas valide".

Quand je fais reader[0].GetType() cela me dit que c'est un Int32. Pour autant que je sache, cela ne devrait pas poser de problème ...

J'ai testé cela par cet extrait qui fonctionne très bien.

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

Cela m'a laissé me gratter la tête en me demandant pourquoi il ne parvient pas à déballer l'int. Contenu dans le lecteur sous forme décimale.

Est-ce que quelqu'un sait pourquoi cela pourrait se produire? Y a-t-il quelque chose de subtil qui me manque?

60
mezoid

Vous pouvez uniquement décompresser un type de valeur vers son type d'origine (et la version nullable de ce type).

Au fait, cela est valable (juste un raccourci pour votre version à deux lignes):

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

Pour la raison derrière cela, lisez ceci Entrée de blog d'Eric Lippert: Représentation et identité

Personnellement, je classe les choses faites par cast syntaxe en quatre types d'opérations différents (ils ont tous des instructions IL différentes):

  1. Boxe (box instruction IL) et unboxing (unbox instruction IL)
  2. Diffusion à travers la hiérarchie d'héritage (comme dynamic_cast<Type> en C++, utilise castclass instruction IL pour vérifier)
  3. Casting entre types primitifs (comme static_cast<Type> en C++, il existe de nombreuses instructions IL pour différents types de transtypages entre types primitifs)
  4. Appel des opérateurs de conversion définis par l'utilisateur (au niveau IL, ce ne sont que des appels de méthode vers le op_XXX méthode).
79
Mehrdad Afshari

Voici une solution simple. Il prend en charge le déballage et le transtypage en décimal. Ça a bien marché pour moi.

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

Il n'y a aucun problème lors de la conversion d'un int en decimal, mais lorsque vous déballez un objet, vous devez utiliser le type exact qu'il contient.

Pour décompresser la valeur int dans une valeur decimal, vous devez d'abord la décompresser en tant qu'int, puis la convertir en décimal:

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

L'interface IDataRecord a également des méthodes pour déballer la valeur:

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

Mehrdad Afshari l'a dit:

Vous pouvez uniquement décompresser un type de valeur vers son type d'origine (et la version nullable de ce type).

La chose à réaliser est que il y a une différence entre le casting et le déballage. jerryjvl a fait une excellente remarque

Dans un sens, il est dommage que le déballage et le casting semblent syntaxiquement identiques, car ce sont des opérations très différentes.

Fonderie:

int i = 3750; // Declares a normal int
decimal d = (decimal)i; // Casts an int into a decimal > OK

Boxe/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?
3
user276648