web-dev-qa-db-fra.com

Types nullables et l'opérateur ternaire: pourquoi `? 10: null` interdit?

Je viens de tomber sur une erreur étrange:

private bool GetBoolValue()
{
    //Do some logic and return true or false
}

Ensuite, dans une autre méthode, quelque chose comme ceci:

int? x = GetBoolValue() ? 10 : null;

Simple, si la méthode retourne true, attribuez 10 à Nullable int x. Sinon, affectez null à nullable int. Cependant, le compilateur se plaint:

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

Est-ce que je vais devenir fou?

240
BFree

Le compilateur essaie d'abord d'évaluer l'expression de droite:

GetBoolValue() ? 10 : null

Le 10 est un int littéral (et non int?) et null est bien null. Il n'y a pas de conversion implicite entre ces deux, d'où le message d'erreur.

Si vous modifiez l'expression de droite pour l'une des suivantes, elle est compilée car il existe une conversion implicite entre int? et null (# 1) et entre int et int? (N ° 2, n ° 3).

GetBoolValue() ? (int?)10 : null    // #1
GetBoolValue() ? 10 : (int?)null    // #2
GetBoolValue() ? 10 : default(int?) // #3
395
LukeH

Essaye ça:

int? x = GetBoolValue() ? 10 : (int?)null;

Fondamentalement, ce qui se passe est que l'opérateur conditionnel est incapable de déterminer le "type de retour" de l'expression. Puisque le compilateur décide implicitement que 10 _ est un int, il décide ensuite que le type de retour de cette expression doit également être un int. Étant donné qu'un int ne peut pas être null (le troisième opérande de l'opérateur conditionnel), se plaint-il.

En convertissant le null en un Nullable<int> nous disons explicitement au compilateur que le type de retour de cette expression doit être un Nullable<int>. Vous auriez pu tout aussi facilement lancer le 10 à int? aussi et a le même effet.

34
Andrew Hare

Essaye ça:

int? result = condition ? 10 : default(int?);

15
Unknown

Incidemment, l'implémentation Microsoft du compilateur C # obtient en fait l'analyse de type de l'opérateur conditionnel de manière très subtile et intéressante (pour moi). Mon article à ce sujet est Type inférence malheurs, première partie.

14
Eric Lippert

Essayez l'une de celles-ci:

int? x = GetBoolValue() ? (int?)10 : null;

int? x = GetBoolValue() ? 10 : (int?)null;
5
John Gietzen

Le problème est que l'opérateur ternaire déduit le type en fonction de votre premier paramétrage ... 10 dans ce cas, qui est un int, pas un int nullable.

Vous pourriez avoir plus de chance avec:

int? x = GetBoolValue() (int?)10 : null;
5
Justin Niessner

Il suffit d'ajouter un casting explicite.

int? x = GetBoolValue() ? 10 : (int?)null;

C’est l’opérateur ternaire qui est confondu - le deuxième argument est un entier et le troisième argument examiné est lui aussi un entier, et la valeur null n’adapte pas.

4
Daniel Brückner

C'est parce que le compilateur détermine le type de l'opérateur conditionnel par ses deuxième et troisième opérandes, et non par ce à quoi vous affectez le résultat. Il n'y a pas de conversion directe entre un entier et une référence null que le compilateur peut utiliser pour déterminer le type.

4
Guffa
int? x = GetBoolValue() ? 10 : (int?)null;

La raison pour laquelle vous voyez cela est parce que vous utilisez Nullable dans les coulisses et que vous devez dire à C # que votre "null" est une instance nulle de Nullable.

4
Martin Peck