Le type d'expression conditionnelle ne peut pas être déterminé car il n'y a pas de conversion implicite entre 'int' et <null>

Réponses:

341

La spécification (§7.14) dit que pour l'expression conditionnelle b ? x : y, il y a trois possibilités, soit xet les ydeux ont un type et certaines bonnes conditions sont remplies, un seul de xet ya un type et certaines bonnes conditions sont remplies, ou une erreur de compilation se produit. Ici, «certaines bonnes conditions» signifie que certaines conversions sont possibles, dont nous entrerons dans les détails ci-dessous.

Passons maintenant à la partie pertinente de la spécification:

Si un seul de xet ya un type, et les deux xet ysont implicitement convertibles en ce type, alors c'est le type de l'expression conditionnelle.

Le problème ici est que dans

int? number = true ? 5 : null;

un seul des résultats conditionnels a un type. Voici xun intlittéral, et yest nullqui n'a pas de type et nulln'est pas implicitement convertible en int1 . Par conséquent, «certaines bonnes conditions» ne sont pas remplies et une erreur de compilation se produit.

Il y a deux façons de contourner cela:

int? number = true ? (int?)5 : null;

Ici, nous sommes toujours dans le cas où un seul de xet ya un type. Notez qu'il n'a null toujours pas de type, le compilateur n'aura aucun problème avec cela car (int?)5et nullsont tous deux implicitement convertibles en int?(§6.1.4 et §6.1.5).

L'autre façon est évidemment:

int? number = true ? 5 : (int?)null;

mais maintenant nous devons lire une clause différente dans la spécification pour comprendre pourquoi c'est correct:

Si xa type Xet ya type Yalors

  • Si une conversion implicite (§6.1) existe de Xvers Y, mais pas de Yvers X, alors Yest le type de l'expression conditionnelle.

  • Si une conversion implicite (§6.1) existe de Yvers X, mais pas de Xvers Y, alors Xest le type de l'expression conditionnelle.

  • Sinon, aucun type d'expression ne peut être déterminé et une erreur de compilation se produit.

Voici xde type intet yest de type int?. Il n'y a pas de conversion implicite de int?à int, mais il y a une conversion implicite de intà int?donc le type de l'expression est int?.

1 : Notez en outre que le type du côté gauche est ignoré dans la détermination du type de l'expression conditionnelle, une source courante de confusion ici.

Jason
la source
4
Bonne citation de la spécification pour illustrer pourquoi cela se produit - +1!
JerKimball
8
Une autre option est new int?()à la place de (int?)null.
Guvante
1
C'est également le cas si vous avez un type de champ de base de données Nullable, par exemple un DateTime Nullable et que vous essayez de convertir des données en DateTime, quand cela est nécessaire(DateTime?)
Mike Upjohn
73

null n'a aucun type identifiable - il a juste besoin d'un petit coup de pouce pour le rendre heureux:

int? number = true ? 5 : (int?)null;
Marc Gravell
la source
3
Ou vous pouvez le faireint? number = true ? 5 : null as int?;
Brad M
Belle réponse clouant le point. Bonne
Benjamin Gruenbaum
Le problème n'est pas qu'il nulln'y ait pas de type identifiable. Le problème est qu'il n'y a pas de conversion implicite de nullvers int. Détails ici .
jason
la chose intéressante est que , int? number = true ? 5 : (int?)null;et les int? number = true ? (int?)5 : null;deux !! compilation Scratch, scratch
davidhq
2
Je couvre exactement pourquoi cela se produit dans ma réponse .
jason
4

Comme d'autres l'ont mentionné, le 5 est un int, et nullne peut pas être implicitement converti en int.

Voici d'autres moyens de contourner le problème:

int? num = true ? 5 : default(int?);
int? num = true ? 5 : new int?();

int? num = true ? 5 : null as int?;
int? num = true ? 5 : (int?)null;

int? num = true ? (int?)5 : null;
int? num = true ? 5 as int? : null;

int? num = true ? new int?(5) : null;

De plus, partout où vous voyez int?, vous pouvez également utiliser Nullable<int>.

Andrew
la source
1

Dans C# 9ce blog est désormais autorisé

Cible typée ?? et ?

Parfois conditionnel ?? et?: les expressions n'ont pas de type partagé évident entre les branches. De tels cas échouent aujourd'hui, mais C # 9.0 les autorisera s'il existe un type de cible vers lequel les deux branches sont converties:

Person person = student ?? customer; // Shared base type
int? result = b ? 0 : null; // nullable value type

Ou votre exemple:

// Allowed in C# 9.
int? number = true ? 5 : null;
WBuck
la source