C #?: Opérateur conditionnel

J’ai cet extrait de code source C # 2.0:

object valueFromDatabase; decimal result; valueFromDatabase = DBNull.Value; result = (decimal)(valueFromDatabase != DBNull.Value ? valueFromDatabase : 0); result = (valueFromDatabase != DBNull.Value ? (decimal)valueFromDatabase : (decimal)0); 

La première évaluation de résultat génère une InvalidCastException alors que la seconde ne le fait pas. Quelle est la différence entre ces deux?

MISE À JOUR: Cette question a fait l’object de mon blog le 27 mai 2010 . Merci pour la bonne question!

Il y a beaucoup de réponses très déroutantes ici. Laissez-moi essayer de répondre précisément à votre question. Simplifions ceci:

 object value = whatever; bool condition = something; decimal result = (decimal)(condition ? value : 0); 

Comment le compilateur interprète-t-il la dernière ligne? Le problème rencontré par le compilateur est que le type de l’expression conditionnelle doit être cohérent pour les deux twigs ; les règles de langage ne vous permettent pas de renvoyer un object sur une twig et int sur l’autre. Les choix sont object et int. Chaque int est convertible en object mais tous les objects ne sont pas convertibles en int, le compilateur choisit donc un object. C’est donc la même chose que

 decimal result = (decimal)(condition ? (object)value : (object)0); 

Par conséquent, le zéro renvoyé est un int encadré.

Vous décodez ensuite l’int en décimal. Il est illégal de déballer une boîte en format décimal. Pour les raisons pourquoi, voir mon article de blog sur ce sujet:

Représentation et identité

En gros, votre problème est que vous agissez comme si la dissortingbution décimale était dissortingbuée, comme ceci:

 decimal result = condition ? (decimal)value : (decimal)0; 

Mais comme nous l’avons vu, ce n’est pas ce que

 decimal result = (decimal)(condition ? value : 0); 

veux dire. Cela signifie que “faire les deux alternatives dans les objects et puis déselectionner l’object résultant”.

La différence est que le compilateur ne peut pas déterminer un type de données correspondant à Object et Int32 .

Vous pouvez explicitement convertir la valeur int en object pour obtenir le même type de données dans le deuxième et le troisième opérande afin de le comstackr, mais celui de couse signifie que vous encaissez et décompressez la valeur:

 result = (decimal)(valueFromDatabase != DBNull.value ? valueFromDatabase : (object)0); 

Cela va comstackr, mais pas courir. Vous devez cocher une valeur décimale pour annuler la décimale:

 result = (decimal)(valueFromDatabase != DBNull.value ? valueFromDatabase : (object)0M); 

Le type de l’opérateur sera object et si le résultat doit être 0, il sera implicitement encadré. Mais 0 littéral a par défaut un type int, donc vous cochez int. Mais avec la conversion explicite en décimale, vous essayez de la décocher, ce qui n’est pas autorisé (le type encadré doit beaucoup avec celui vers lequel vous retournez). C’est pourquoi vous pouvez obtenir une exception.

Voici un extrait de la spécification C #:

Les deuxième et troisième opérandes de l’opérateur?: Contrôlent le type de l’expression conditionnelle. Soit X et Y les types des deuxième et troisième opérandes. Alors,

  • Si X et Y sont du même type, alors c’est le type de l’expression conditionnelle.
  • Sinon, si une conversion implicite (§6.1) existe de X à Y, mais pas de Y à X, alors Y est le type de l’expression conditionnelle.
  • Sinon, si une conversion implicite (§6.1) existe de Y à X, mais pas de X à Y, alors X est le type de l’expression conditionnelle.
  • Sinon, aucun type d’expression ne peut être déterminé et une erreur de compilation se produit.

Votre ligne devrait être:

 result = valueFromDatabase != DBNull.value ? (decimal)valueFromDatabase : 0m; 

0m est la constante décimale pour zéro

Les deux parties d’un opérateur conditionnel doivent évaluer le même type de données

La partie x: y nécessite un type commun, la valeur de la firebase database est probablement une sorte de float et 0 est un int. Cela se produit avant que la dissortingbution ne soit décimale. Essayez “: 0.0” ou “: 0D”.

A moins que je ne me trompe (ce qui est très possible), il s’agit en fait du 0 qui cause l’exception, et ceci est dû à .NET (fou) en supposant le type d’un littéral, vous devez donc spécifier 0m plutôt que 0.

Voir MSDN pour plus d’informations.

Il existe deux types différents pour que le compilateur décide (au moment de la compilation) lequel il faut convertir en décimal. Cela ne peut pas faire.

Votre réponse fonctionnerait si vous combiniez les deux:

 result = (decimal)(valueFromDatabase != DBNull.Value ? (decimal)valueFromDatabase : (decimal)0); 

Au moins, une situation similaire se fondant sur un paramètre pour moi.