Jenis masalah yang tidak dapat dibatalkan dengan?: Operator Bersyarat

154

Bisakah seseorang menjelaskan mengapa ini bekerja di C # .NET 2.0:

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

... tapi ini tidak:

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

Bentuk terakhir memberi saya kesalahan kompilasi "Jenis ekspresi kondisional tidak dapat ditentukan karena tidak ada konversi implisit antara '<null>' dan 'System.DateTime'."

Bukannya saya tidak bisa menggunakan yang pertama, tetapi gaya kedua lebih konsisten dengan sisa kode saya.

Nick Gotch
sumber
12
Anda dapat menghemat banyak pengetikan dengan menggunakan DateTime? alih-alih Nullable <DateTime>.
Stewart Johnson

Jawaban:

325

Pertanyaan ini sudah ditanyakan beberapa kali. Compiler memberitahu Anda bahwa ia tidak tahu bagaimana mengubahnya nullmenjadi DateTime.

Solusinya sederhana:

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

Catatan yang Nullable<DateTime>dapat ditulis DateTime?yang akan menghemat banyak mengetik.

Stewart Johnson
sumber
Berfungsi cukup baik tetapi sekarang Anda tidak dapat membatalkan pemeriksaan foo - ini akan selalu memiliki nilai. Tidak ada jalan lain meskipun - seperti MojoFilter mengatakan "Itu karena dalam operator ternary, dua nilai harus menjadi tipe yang sama."
DilbertDave
@DilbertDave Informasi dari pos MojoFilter salah.
Mishax
4
Saya akan menambahkan bahwa kompiler mencoba untuk menebak jenis yang dihasilkan dari operasi ternary bukan dengan melihat variabel yang ditugaskan, tetapi dengan melihat operan sebagai gantinya. Ia menemukan <null>dan DateTimebukannya menemukan tipe leluhur yang sama, ia hanya mencoba untuk menemukan pertobatan di antara yang lain. (Bit ekstra: C # mengenali <null>tipe, yaitu tipe setiap nullekspresi.)
IllidanS4 ingin Monica kembali pada
Jika sudah ditanyakan beberapa kali, di mana bendera pertanyaan rangkap?
starmandeluxe
@starmandeluxe mereka semua kemungkinan menunjuk ke sini (setidaknya saya tahu bagaimana saya sampai di sini)
Scott Chamberlain
19

FYI (Offtopic, tapi bagus dan terkait dengan tipe nullable) kami memiliki operator yang berguna hanya untuk tipe nullable yang disebut operator penggabungan nol

??

Digunakan seperti ini:

// Left hand is the nullable type, righthand is default if the type is null.
Nullable<DateTime> foo;
DateTime value = foo ?? new DateTime(0);
FlySwat
sumber
9
Bagaimana ini menjawab pertanyaannya ??
Stewart Johnson
3
Nick sedang mencoba menetapkan null to foo jika beberapa kondisi benar. Null coalesce akan menetapkan DateTime (0) ke nilai jika foo bernilai nol. Keduanya sama sekali tidak berhubungan.
Jeromy Irvine
4
Oleh karena itu FYI, offtopic tetapi hal yang menyenangkan untuk diketahui.
FlySwat
Ah, baiklah. Cukup berguna untuk diketahui.
Jeromy Irvine
8

Itu karena di operator ternary, dua nilai harus menyelesaikan ke tipe yang sama.

MojoFilter
sumber
10
Tidak, mereka tidak harus berjenis sama. Entah operan kedua harus secara implisit dapat dikonversi ke jenis operan ketiga atau sebaliknya.
Mishax
3

Saya tahu pertanyaan ini ditanyakan pada tahun 2008 dan sekarang 5 tahun kemudian tetapi jawaban yang ditandai sebagai jawaban tidak memuaskan saya. Jawaban sebenarnya adalah DateTime adalah sebuah struct, dan sebagai sebuah struct, itu tidak kompatibel dengan null. Anda memiliki dua cara untuk menyelesaikannya:

Pertama adalah membuat null kompatibel dengan DateTime (misalnya, masukkan null ke DateTime? Seperti yang dikatakan oleh pria dengan 70 upvotes, atau nol untuk Object atau ValueType).

Yang kedua adalah untuk membuat DateTime kompatibel dengan nol (misalnya, melemparkan DateTime ke DateTime?).

Mishax
sumber
3

Solusi lain yang mirip dengan yang diterima adalah dengan menggunakan defaultkata kunci C # . Meskipun didefinisikan menggunakan obat generik, sebenarnya ini berlaku untuk semua jenis.

Contoh penggunaan yang diterapkan pada pertanyaan OP:

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

Contoh penggunaan dengan jawaban yang diterima saat ini:

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

Selain itu, dengan menggunakan default, Anda tidak perlu menentukan variabel seperti nullableuntuk memberikan nullnilai. Kompiler akan secara otomatis menetapkan nilai default tipe variabel tertentu dan tidak akan ada kesalahan yang terjadi. Contoh:

DateTime foo;
foo = true ? default(DateTime) : new DateTime(0);
perabotan baru
sumber
13
Tidak benar, default(DateTime)bukan nol, ini " 1.1.0001 0:00:00", sama dengan new DateTime(0).
IllidanS4 ingin Monica kembali
@ IllidanS4, saya tidak mengatakan bahwa itu sama dengan null, hanya dengan menggunakan default()Anda dapat menetapkannya ke nullablenilai (seperti yang dinyatakan MSDN). Contoh saya menunjukkan menunjukkan fleksibilitas yang dapat digunakan dengan Nullable<DateTime>, DateTime?, dan hanya DateTime. Jika Anda yakin ini tidak benar, dapatkah Anda memberikan PoC jika gagal?
perabot baru
3
Nah, si penanya ingin menyimpan nulldalam variabel, bukan default(DateTime), jadi ini menyesatkan terbaik. Ini bukan "serbaguna" seperti yang Anda maksudkan, karena ekspresi secara keseluruhan masih memiliki tipe yang sama - DateTime, dan Anda dapat menggantinya default(DateTime)dengan new DateTime()dan itu akan melakukan hal yang sama. Mungkin default(DateTime?)itu yang Anda maksudkan, karena itu sebenarnya sama dengan null.
IllidanS4 ingin Monica kembali pada