Problem z typem dopuszczającym wartość null z?: Operator warunkowy

154

Czy ktoś mógłby wyjaśnić, dlaczego to działa w C # .NET 2.0:

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

... ale to nie:

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

Druga forma powoduje błąd kompilacji „Nie można określić typu wyrażenia warunkowego, ponieważ nie ma niejawnej konwersji między '<null>' i 'System.DateTime'.”

Nie to, że nie mogę użyć pierwszego, ale drugi styl jest bardziej spójny z resztą mojego kodu.

Nick Gotch
źródło
12
Możesz zaoszczędzić sobie dużo pisania, używając DateTime? zamiast Nullable <DateTime>.
Stewart Johnson

Odpowiedzi:

325

To pytanie zadawano już kilka razy. Kompilator mówi ci, że nie wie, jak przekonwertować nullplik DateTime.

Rozwiązanie jest proste:

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

Zauważ, że Nullable<DateTime>można napisać, DateTime?co pozwoli Ci zaoszczędzić sporo pisania.

Stewart Johnson
źródło
Działa wystarczająco dobrze, ale teraz nie możesz zerować foo - zawsze będzie miało wartość. Nie można tego jednak obejść - jak mówi MojoFilter „Dzieje się tak, ponieważ w operatorze trójskładnikowym te dwie wartości muszą być tego samego typu”.
DilbertDave,
@DilbertDave Informacje z posta MojoFilter są nieprawidłowe.
Mishax
4
Dodałbym, że kompilator próbuje odgadnąć wynikowy typ operacji trójskładnikowej nie patrząc na zmienną, do której jest przypisany, ale patrząc na operandy. Okazuje <null>a DateTimei zamiast znalezienia wspólnego przodka rodzaju, po prostu próbuje znaleźć konwersję między sobą. (Dodatkowy bit: C # rozpoznaje <null>typ, tj. Typ każdego nullwyrażenia.)
IllidanS4 chce, aby Monica wróciła
Jeśli zostało to już zadane kilka razy, gdzie jest duplikat flagi pytania?
starmandeluxe
@starmandeluxe wszyscy prawdopodobnie wskazują tutaj (przynajmniej to, jak się tu dostałem)
Scott Chamberlain
19

FYI (Offtopic, but sprytny i powiązany z typami dopuszczającymi wartość null) mamy przydatny operator tylko dla typów dopuszczających wartość null zwany null coalescing operator

??

Używane w ten sposób:

// Left hand is the nullable type, righthand is default if the type is null.
Nullable<DateTime> foo;
DateTime value = foo ?? new DateTime(0);
FlySwat
źródło
9
Jak to odpowiada na jego pytanie?
Stewart Johnson
3
Nick próbuje przypisać wartość null do foo, jeśli jakiś warunek jest prawdziwy. Łączenie wartości null przypisze DateTime (0) do wartości, jeśli foo jest null. Obaj są całkowicie niezwiązani.
Jeromy Irvine
4
Stąd FYI, offtopic, ale miło wiedzieć.
FlySwat
Ach, dobrze. Warto wiedzieć.
Jeromy Irvine
8

Dzieje się tak, ponieważ w operatorze trójargumentowym dwie wartości muszą być rozstrzygane na ten sam typ.

MojoFilter
źródło
10
Nie, nie muszą być tego samego typu. Albo drugi operand musi być niejawnie konwertowany na typ trzeciego operandu lub na odwrót.
Mishax,
3

Wiem, że to pytanie padło w 2008 roku, a teraz jest 5 lat później, ale odpowiedź oznaczona jako odpowiedź mnie nie satysfakcjonuje. Prawdziwą odpowiedzią jest to, że DateTime jest strukturą i jako struktura nie jest zgodna z wartością null. Masz dwa sposoby rozwiązania tego problemu:

Po pierwsze, należy uczynić null kompatybilnym z DateTime (na przykład rzut null na DateTime ?, jak sugeruje dżentelmen z 70 upvotes, lub rzut null na Object lub ValueType).

Drugim jest zapewnienie zgodności DateTime z wartością null (na przykład rzutowanie DateTime na DateTime?).

Mishax
źródło
3

Innym rozwiązaniem podobnym do przyjętego jest użycie defaultsłowa kluczowego C # . Chociaż jest zdefiniowany przy użyciu typów ogólnych, w rzeczywistości ma zastosowanie do każdego typu.

Przykładowe użycie zastosowane do pytania PO:

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

Przykładowe użycie z aktualnie zaakceptowaną odpowiedzią:

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

Ponadto, korzystając z default, nie musisz określać zmiennej jako nullable, aby przypisać jej nullwartość. Kompilator automatycznie przypisze domyślną wartość określonego typu zmiennej i nie zostanie napotkany żaden błąd. Przykład:

DateTime foo;
foo = true ? default(DateTime) : new DateTime(0);
newfurniturey
źródło
13
Nieprawda, default(DateTime)nie jest zerowa, to jest " 1.1.0001 0:00:00" to samo co new DateTime(0).
IllidanS4 chce, aby Monica wróciła
@ IllidanS4, nie powiedziałem, że jest równe null, tylko że używając default()możesz przypisać go do nullablewartości (jak stwierdza MSDN). Przykłady pokazują, że wykazują różnorodność, która może być używana z Nullable<DateTime>, DateTime?i po prostu DateTime. Jeśli uważasz, że to nieprawda, czy możesz dostarczyć dokument PoC, jeśli te zawiodły?
newfurniturey
3
Cóż, pytający chciał przechowywać nullw zmiennej, nie default(DateTime), więc jest to w najlepszym przypadku mylące. To nie jest „uniwersalny”, jak sugerujesz, ponieważ wyrażenie jako całość ma jeszcze tego samego typu - DateTimei można zastąpić default(DateTime)z new DateTime()i będzie robić to samo. Może default(DateTime?)to miałeś na myśli, skoro tak naprawdę jest to równe null.
IllidanS4 chce, aby Monica wróciła