Problème de type Nullable avec?: Conditional Operator

154

Quelqu'un pourrait-il expliquer pourquoi cela fonctionne en C # .NET 2.0:

    Nullable<DateTime> foo;
    if (true)
        foo = null;
    else
        foo = new DateTime(0);

... mais ce n'est pas le cas:

    Nullable<DateTime> foo;
    foo = true ? null : new DateTime(0);

Cette dernière forme me donne une erreur de compilation "Le type d'expression conditionnelle ne peut pas être déterminé car il n'y a pas de conversion implicite entre '<null>' et 'System.DateTime'."

Non pas que je ne puisse pas utiliser l'ancien, mais le second style est plus cohérent avec le reste de mon code.

Nick Gotch
la source
12
Vous pouvez vous épargner beaucoup de saisie en utilisant DateTime? au lieu de Nullable <DateTime>.
Stewart Johnson

Réponses:

325

Cette question a déjà été posée plusieurs fois. Le compilateur vous dit qu'il ne sait pas comment convertir nullun fichier DateTime.

La solution est simple:

DateTime? foo;
foo = true ? (DateTime?)null : new DateTime(0);

Notez que cela Nullable<DateTime>peut être écrit, DateTime?ce qui vous évitera beaucoup de frappe.

Stewart Johnson
la source
Fonctionne assez bien mais maintenant vous ne pouvez pas annuler la vérification foo - elle aura toujours une valeur. Pas moyen de contourner cela cependant - comme le dit MojoFilter "C'est parce que dans un opérateur ternaire, les deux valeurs doivent être du même type."
DilbertDave
@DilbertDave Les informations du message de MojoFilter sont incorrectes.
Mishax
4
J'ajouterais que le compilateur essaie de deviner le type résultant de l'opération ternaire non pas en regardant la variable à laquelle elle est assignée, mais en regardant les opérandes à la place. Il trouve <null>et DateTimeet au lieu de trouver le type d'ancêtre commun, il essaie simplement de trouver une conversion entre eux. (Bit supplémentaire: C # reconnaît un <null>type, c'est-à-dire le type de chaque nullexpression.)
IllidanS4 veut que Monica revienne
Si cela a déjà été posé plusieurs fois, où se trouve l'indicateur de question en double?
starmandeluxe
@starmandeluxe, ils indiquent probablement tous ici (du moins que je suis arrivé ici)
Scott Chamberlain
19

FYI (Offtopic, mais astucieux et lié aux types Nullable), nous avons un opérateur pratique juste pour les types Nullable appelé l'opérateur de fusion null

??

Utilisé comme ceci:

// Left hand is the nullable type, righthand is default if the type is null.
Nullable<DateTime> foo;
DateTime value = foo ?? new DateTime(0);
FlySwat
la source
9
Comment cela répond-il à sa question ??
Stewart Johnson
3
Nick essaie d'assigner null à foo si une condition est vraie. La fusion nulle attribuera DateTime (0) à la valeur si foo est nul. Les deux ne sont absolument pas liés.
Jeromy Irvine
4
D'où le FYI, hors-sujet mais une bonne chose à savoir.
FlySwat
Ah ok. C'est assez utile de savoir.
Jeromy Irvine
8

C'est parce que dans un opérateur ternaire, les deux valeurs doivent se résoudre au même type.

MojoFilter
la source
10
Non, ils ne doivent pas nécessairement être du même type. Soit le deuxième opérande doit être implicitement convertible en type du troisième opérande, soit l'inverse.
Mishax
3

Je sais que cette question a été posée en 2008 et c'est maintenant 5 ans plus tard mais la réponse marquée comme réponse ne me satisfait pas. La vraie réponse est que DateTime est une structure et qu'en tant que structure, elle n'est pas compatible avec null. Vous avez deux façons de résoudre ce problème:

La première consiste à rendre null compatible avec DateTime (par exemple, convertir null en DateTime? Comme le suggère le gentleman avec 70 votes positifs, ou convertir null en Object ou ValueType).

La seconde est de rendre le DateTime compatible avec null (par exemple, transtyper DateTime en DateTime?).

Mishax
la source
3

Une autre solution similaire à celle acceptée consiste à utiliser le defaultmot clé de C # . Bien que défini à l'aide de génériques, il est en fait applicable à n'importe quel type.

Exemple d'utilisation appliquée à la question du PO:

Nullable<DateTime> foo;
foo = true ? default(DateTime) : new DateTime(0);

Exemple d'utilisation avec la réponse acceptée actuelle:

DateTime? foo;
foo = true ? default(DateTime) : new DateTime(0);

De plus, en utilisant default, vous n'avez pas besoin de spécifier la variable nullablepour lui attribuer une nullvaleur. Le compilateur attribuera automatiquement la valeur par défaut du type de variable spécifique et aucune erreur ne sera rencontrée. Exemple:

DateTime foo;
foo = true ? default(DateTime) : new DateTime(0);
nouveau meuble
la source
13
Ce default(DateTime)n'est pas vrai, n'est pas nul, c'est " 1.1.0001 0:00:00", le même que new DateTime(0).
IllidanS4 veut que Monica revienne
@ IllidanS4, je n'ai pas dit que c'était égal à null, seulement qu'en utilisant default()vous pouvez l'attribuer à une nullablevaleur (comme l'indique MSDN). Les exemples que je montre démontrent la polyvalence qu'il peut être utilisé avec Nullable<DateTime>, DateTime?et tout simplement DateTime. Si vous pensez que cela est incorrect, pouvez-vous fournir un PoC là où ils échouent?
newfurniturey
3
Eh bien, le questionneur voulait stocker nulldans la variable, non default(DateTime), donc c'est au mieux trompeur. Ce n'est pas "polyvalent" comme vous le sous-entendez, puisque l'expression dans son ensemble a toujours le même type - DateTime, et vous pouvez la remplacer default(DateTime)par new DateTime()et elle fera la même chose. C'est peut default(DateTime?)- être ce que vous vouliez dire, puisque c'est en fait égal à null.
IllidanS4 veut que Monica revienne