Nullable Typ Problem mit ?: Bedingter Operator

154

Könnte jemand erklären, warum dies in C # .NET 2.0 funktioniert:

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

... aber das geht nicht:

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

Das letztere Formular gibt mir einen Kompilierungsfehler. "Der Typ des bedingten Ausdrucks kann nicht bestimmt werden, da keine implizite Konvertierung zwischen '<null>' und 'System.DateTime' erfolgt."

Nicht, dass ich den ersteren nicht verwenden kann, aber der zweite Stil stimmt besser mit dem Rest meines Codes überein.

Nick Gotch
quelle
12
Mit DateTime können Sie sich viel Tipparbeit sparen? anstelle von Nullable <DateTime>.
Stewart Johnson

Antworten:

325

Diese Frage wurde bereits einige Male gestellt. Der Compiler sagt Ihnen, dass er nicht weiß, wie er nullin einen konvertiertDateTime .

Die Lösung ist einfach:

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

Beachten Sie, dass Nullable<DateTime>dies geschrieben DateTime?werden kann, wodurch Sie eine Menge Tipparbeit sparen.

Stewart Johnson
quelle
Funktioniert gut genug, aber jetzt können Sie foo nicht auf Null setzen - es wird immer einen Wert haben. Daran führt kein Weg vorbei - wie MojoFilter sagt: "In einem ternären Operator müssen die beiden Werte vom gleichen Typ sein."
DilbertDave
@DilbertDave Die Informationen aus MojoFilters Beitrag sind falsch.
Mishax
4
Ich möchte hinzufügen, dass der Compiler versucht, den resultierenden Typ der ternären Operation zu erraten, indem er nicht die Variable betrachtet, der er zugewiesen ist, sondern stattdessen die Operanden. Es findet <null>und DateTimeund anstatt den gemeinsamen Ahnen-Typ zu finden, versucht es nur, eine Konvertierung untereinander zu finden. (Extra-Bit: C # erkennt einen <null>Typ, dh den Typ jedes nullAusdrucks.)
IllidanS4 will Monica
Wenn es schon ein paar Mal gefragt wurde, wo ist das Flag für doppelte Fragen?
Starmandeluxe
@starmandeluxe sie alle zeigen wahrscheinlich hier (zumindest, dass ich, wie ich hierher gekommen bin)
Scott Chamberlain
19

Zu Ihrer Information (Offtopic, aber geschickt und verwandt mit nullbaren Typen) haben wir einen praktischen Operator nur für nullbare Typen, den Null-Koaleszenz-Operator

??

So verwendet:

// Left hand is the nullable type, righthand is default if the type is null.
Nullable<DateTime> foo;
DateTime value = foo ?? new DateTime(0);
FlySwat
quelle
9
Wie beantwortet dies seine Frage?
Stewart Johnson
3
Nick versucht, foo null zuzuweisen, wenn eine Bedingung erfüllt ist. Die Null-Koaleszenz weist DateTime (0) dem Wert zu, wenn foo null ist. Die beiden sind völlig unabhängig.
Jeromy Irvine
4
Daher die FYI, offtopic, aber eine schöne Sache zu wissen.
FlySwat
Ah, OK. Es ist ziemlich nützlich zu wissen.
Jeromy Irvine
8

Dies liegt daran, dass in einem ternären Operator die beiden Werte in denselben Typ aufgelöst werden müssen.

MojoFilter
quelle
10
Nein, sie müssen nicht vom selben Typ sein. Entweder muss der zweite Operand implizit in den Typ des dritten Operanden konvertierbar sein oder umgekehrt.
Mishax
3

Ich weiß, dass diese Frage im Jahr 2008 gestellt wurde und es nun 5 Jahre später ist, aber die als Antwort gekennzeichnete Antwort befriedigt mich nicht. Die eigentliche Antwort lautet, dass DateTime eine Struktur ist und als Struktur nicht mit null kompatibel ist. Sie haben zwei Möglichkeiten, dies zu lösen:

Zunächst muss null mit DateTime kompatibel gemacht werden (z. B. null in DateTime umwandeln, wie der Gentleman mit 70 positiven Stimmen vorschlägt, oder null in Object oder ValueType umwandeln).

Die zweite besteht darin, die DateTime mit null kompatibel zu machen (z. B. DateTime in DateTime umwandeln?).

Mishax
quelle
3

Eine andere ähnliche Lösung wie die akzeptierte besteht darin, das defaultSchlüsselwort von C # zu verwenden . Während es mit Generika definiert wird, ist es tatsächlich auf jeden Typ anwendbar.

Anwendungsbeispiel für die Frage des OP:

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

Anwendungsbeispiel mit der aktuell akzeptierten Antwort:

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

Bei Verwendung von defaultmüssen Sie die Variable auch nicht wie angegeben angeben nullable, um ihr einen nullWert zuzuweisen . Der Compiler weist den Standardwert des jeweiligen Variablentyps automatisch zu, und es tritt kein Fehler auf. Beispiel:

DateTime foo;
foo = true ? default(DateTime) : new DateTime(0);
neue Möbel
quelle
13
Nicht wahr, default(DateTime)ist nicht null, es ist " 1.1.0001 0:00:00", das gleiche wie new DateTime(0).
IllidanS4 will Monica
@ IllidanS4, ich habe nicht gesagt, dass es gleich ist null, nur dass default()Sie es mithilfe von verwenden können, um es einem nullableWert zuzuweisen (wie MSDN angibt). Die Beispiele , die ich zeigen , demonstrieren die Vielseitigkeit , die sie mit verwendet werden kann Nullable<DateTime>, DateTime?und einfach DateTime. Wenn Sie der Meinung sind, dass dies falsch ist, können Sie einen PoC bereitstellen, bei dem diese fehlschlagen?
Newfurniturey
3
Nun, der Fragesteller wollte nullin der Variablen speichern , nicht default(DateTime), also ist dies bestenfalls irreführend. Dies ist nicht „vielseitig“ , wie Sie implizieren, da der Ausdruck als Ganzes noch die gleiche Art hat - DateTimeund Sie können ersetzen default(DateTime)mit new DateTime()und es wird das gleiche tun. Vielleicht haben default(DateTime?)Sie das gemeint, denn das ist eigentlich gleich null.
IllidanS4 will Monica