Apa arti dari dua tanda tanya bersama dalam C #?


1632

Berlari melintasi baris kode ini:

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

Apa arti dari dua tanda tanya, apakah itu semacam operator ternary? Sulit untuk mencari di Google.


50
Ini jelas bukan operator ternary - hanya memiliki dua operan! Ini agak seperti operator kondisional (yang merupakan terner) tetapi operator penggabungan nol adalah operator biner.
Jon Skeet

90
Saya menjelaskannya dalam sebuah wawancara di mana calon majikan sebelumnya menyatakan keraguan tentang kemampuan C # saya, karena saya telah menggunakan Java secara profesional untuk sementara waktu sebelumnya. Mereka tidak pernah mendengar itu sebelumnya, dan tidak mempertanyakan keakraban saya dengan C # setelah itu :)
Jon Skeet

77
@Jon Skeet Belum ada epik yang gagal mengenali keterampilan sejak pria yang menolak The Beatles. :-) Mulai sekarang cukup kirimkan salinan buku Anda kepada mereka dengan tautan url ke profil SO Anda yang tertulis di sampul bagian dalam.
Iain Holder

6
IainMH: Untuk apa itu layak, aku tidak cukup mulai menulis buku belum. (Atau mungkin aku hanya bekerja pada bab 1 -. Sesuatu seperti itu) Diakui pencarian untuk saya akan cepat menemukan blog saya + artikel dll
Jon Skeet

7
Re: kalimat terakhir dalam q - untuk ref di masa depan, SymbolHound sangat bagus untuk hal semacam ini misalnya symbolhound.com/?q=%3F%3F&l=&e=&n=&u= [kepada siapa pun yang mencurigakan - Saya tidak berafiliasi dengan Bagaimanapun juga, seperti alat yang bagus ketika saya menemukannya ...]
Steve Chambers

Jawaban:


2156

Ini adalah operator penggabungan nol, dan sangat mirip dengan operator ternary (langsung-jika). Lihat juga ?? Operator - MSDN .

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

memperluas ke:

FormsAuth = formsAuth != null ? formsAuth : new FormsAuthenticationWrapper();

yang selanjutnya berkembang menjadi:

if(formsAuth != null)
    FormsAuth = formsAuth;
else
    FormsAuth = new FormsAuthenticationWrapper();

Dalam bahasa Inggris, itu berarti "Jika apa pun yang ada di sebelah kiri bukan nol, gunakan itu, kalau tidak gunakan apa yang ke kanan."

Perhatikan bahwa Anda dapat menggunakan nomor-nomor ini secara berurutan. Pernyataan berikut akan menetapkan non-null pertama Answer#ke Answer(jika semua Jawaban adalah nol maka itu Answeradalah nol):

string Answer = Answer1 ?? Answer2 ?? Answer3 ?? Answer4;

Juga perlu disebutkan sementara ekspansi di atas secara konseptual setara, hasil dari setiap ekspresi hanya dievaluasi satu kali. Ini penting jika misalnya ekspresi adalah pemanggilan metode dengan efek samping. (Kredit untuk @ Joey karena menunjukkan ini.)


17
Berpotensi membahayakan rantai ini
Coops

6
@ KodeBlend bagaimana?
lc.

68
@ KodeBlend, itu tidak berbahaya. Jika Anda memperluas, Anda hanya akan memiliki serangkaian pernyataan if / else bersarang. Sintaksnya hanya aneh karena Anda tidak terbiasa melihatnya.
joelmdev

31
Itu akan menjadi pengiriman dewa jika itu bekerja untuk string kosong juga :)
Zyphrax

14
@Gdor ??adalah asosiatif kiri, jadi a ?? b ?? c ?? dsama dengan ((a ?? b) ?? c ) ?? d. "Operator penugasan dan operator ternary (? :) adalah asosiatif yang tepat. Semua operator biner lainnya adalah asosiatif kiri." Sumber: msdn.microsoft.com/en-us/library/ms173145.aspx
Mark E. Haase

284

Hanya karena tidak ada orang lain yang mengucapkan kata-kata ajaib: itu adalah operator penggabungan nol . Ini didefinisikan dalam bagian 7.12 dari spesifikasi bahasa C # 3.0 .

Ini sangat berguna, terutama karena cara kerjanya ketika digunakan beberapa kali dalam ekspresi. Ekspresi bentuk:

a ?? b ?? c ?? d

akan memberikan hasil ekspresi ajika tidak nol, jika tidak coba b, jika tidak coba c, jika tidak coba d. Ini korsleting di setiap titik.

Juga, jika jenis dtidak dapat dibatalkan, jenis seluruh ekspresi juga tidak dapat dibatalkan.


2
Saya suka bagaimana Anda menyederhanakan maknanya menjadi "operator penggabungan nol". Itulah yang saya butuhkan.
FrankO

2
Tangensial tetapi terkait: Sekarang di Swift, tetapi sangat berbeda: "Nil Coalescing Operator" developer.apple.com/library/ios/documentation/Swift/Conceptual/…
Dan Rosenstark


27

Terima kasih semuanya, berikut adalah penjelasan paling ringkas yang saya temukan di situs MSDN:

// y = x, unless x is null, in which case y = -1.
int y = x ?? -1;

4
Ini mengisyaratkan aspek penting dari ?? Operator - diperkenalkan untuk membantu bekerja dengan tipe nullable. Dalam contoh Anda, "x" bertipe "int?" (Tidak dapat dihapus <int>).
Drew Noakes

9
@vitule no, jika operan kedua dari operator penggabungan nol tidak dapat dibatalkan, maka hasilnya tidak dapat dibatalkan (dan -1hanya sebuah dataran int, yang tidak dapat dibatalkan).
Suzanne Dupéron

Saya benar-benar berharap itu berhasil seperti ini. Saya ingin melakukan ini sepanjang waktu, tetapi saya tidak bisa karena variabel yang saya gunakan bukan tipe yang dapat dibatalkan. Bekerja dalam coffeescript y = x? -1
PandaWood

@ Pamanda Sebenarnya, Anda bisa. Jika xtipe int?, tetapi ytipe int, Anda bisa menulis int y = (int)(x ?? -1). Ini akan diuraikan xmenjadi intjika tidak null, atau ditugaskan -1ke yjika xtidak null.
Johan Wintgens

21

??ada untuk memberikan nilai untuk jenis yang dapat dibatalkan ketika nilainya nol. Jadi, jika formAuth adalah nol, itu akan mengembalikan FormsAuthenticationWrapper () yang baru.


19

masukkan deskripsi gambar di sini

Dua tanda tanya (??) menunjukkan bahwa ini adalah operator Penggabungan.

Operator penggabungan mengembalikan nilai NON-NULL pertama dari sebuah rantai. Anda dapat melihat video youtube ini yang menunjukkan semuanya secara praktis.

Tetapi izinkan saya menambahkan lebih banyak pada apa yang dikatakan video.

Jika Anda melihat arti bahasa Inggris penggabungan itu mengatakan "konsolidasi bersama". Sebagai contoh di bawah ini adalah kode penggabungan sederhana yang menghubungkan empat string.

Jadi jika str1ini nullia akan mencoba str2, jika str2ini nullia akan mencoba str3dan seterusnya sampai menemukan string dengan nilai non-null.

string final = str1 ?? str2 ?? str3 ?? str4;

Dengan kata sederhana, operator Penggabungan mengembalikan nilai NON-NULL pertama dari sebuah rantai.


Saya suka penjelasan ini karena memiliki diagram, dan kemudian kalimat terakhir menjelaskannya dengan istilah yang sangat sederhana! Itu membuatnya mudah untuk memahami dan meningkatkan kepercayaan diri saya dalam menggunakan. Terima kasih!
Joshua K

14

Ini tangan pendek untuk operator ternary.

FormsAuth = (formsAuth != null) ? formsAuth : new FormsAuthenticationWrapper();

Atau bagi mereka yang tidak melakukan ternary:

if (formsAuth != null)
{
  FormsAuth = formsAuth;
}
else
{
  FormsAuth = new FormsAuthenticationWrapper();
}

3
Saya hanya mengoreksi ejaan "ternary" tetapi sebenarnya operator yang Anda maksud adalah operator bersyarat. Itu kebetulan menjadi satu-satunya operator ternary di C #, tetapi pada titik tertentu mereka mungkin menambahkan yang lain, di mana "ternary" akan ambigu tetapi "kondisional" tidak akan.
Jon Skeet

Ini adalah singkatan untuk sesuatu yang dapat Anda lakukan dengan operator ternary (bersyarat). Dalam bentuk panjang Anda, baik tes ( != null) dan yang kedua formsAuth(setelah ?) dapat diubah; dalam bentuk penggabungan nol, keduanya secara implisit mengambil nilai yang Anda berikan.
Bob Sammers

12

Jika Anda terbiasa dengan Ruby, ||=sepertinya mirip dengan C # ??bagi saya. Ini beberapa Ruby:

irb(main):001:0> str1 = nil
=> nil
irb(main):002:0> str1 ||= "new value"
=> "new value"
irb(main):003:0> str2 = "old value"
=> "old value"
irb(main):004:0> str2 ||= "another new value"
=> "old value"
irb(main):005:0> str1
=> "new value"
irb(main):006:0> str2
=> "old value"

Dan di C #:

string str1 = null;
str1 = str1 ?? "new value";
string str2 = "old value";
str2 = str2 ?? "another new value";

4
x ||= ydesugars ke sesuatu seperti x = x || y, jadi ??sebenarnya lebih mirip dengan polos ||di Ruby.
qerub

6
Perhatikan bahwa ??hanya peduli tentang null, sedangkan ||operator dalam Ruby, seperti dalam kebanyakan bahasa, adalah tentang null, false, atau apapun yang dapat dianggap sebagai boolean dengan nilai false(misalnya dalam beberapa bahasa, ""). Ini bukan hal yang baik atau buruk, hanya perbedaan.
Tim S.

11

operator penggabungan

itu setara dengan

FormsAuth = formsAUth == null ? new FormsAuthenticationWrapper() : formsAuth

11

Tidak ada yang berbahaya tentang ini. Padahal, itu indah. Anda dapat menambahkan nilai default jika diinginkan, misalnya:

KODE

int x = x1 ?? x2 ?? x3 ?? x4 ?? 0;

1
Jadi x1, x2, x3, dan x4 bisa menjadi tipe Nullable, contoh: int? x1 = null;Apakah itu benar
Kevin Meredith

1
@KevinMeredith x1- x4HARUS menjadi jenis yang dapat dibatalkan : tidak masuk akal untuk mengatakan, secara efektif, "hasilnya adalah 0jika x4nilai yang tidak mungkin diambil" ( null). "Jenis yang dapat dihapus" di sini mencakup jenis nilai yang dapat dibatalkan dan jenis referensi, tentu saja. Ini adalah kesalahan waktu kompilasi jika satu atau lebih variabel dirantai (kecuali yang terakhir) tidak dapat dibatalkan.
Bob Sammers

11

Seperti yang ditunjukkan dengan benar dalam berbagai jawaban yang merupakan "operator penggabungan nol" ( ?? ), yang berbicara tentang Anda mungkin juga ingin memeriksa sepupunya "Operator Bersyarat Null" ( ?. Atau ? [ ) Yang merupakan operator yang banyak kali digunakan dalam hubungannya dengan ??

Operator tidak bersyarat

Digunakan untuk menguji nol sebelum melakukan operasi akses anggota ( ? ) Atau indeks ( ? [ ). Operator-operator ini membantu Anda menulis lebih sedikit kode untuk menangani cek nol, terutama untuk turun ke struktur data.

Sebagai contoh:

// if 'customers' or 'Order' property or 'Price' property  is null,
// dollarAmount will be 0 
// otherwise dollarAmount will be equal to 'customers.Order.Price'

int dollarAmount = customers?.Order?.Price ?? 0; 

cara lama tanpa ? dan ?? melakukan ini

int dollarAmount = customers != null 
                   && customers.Order!=null
                   && customers.Order.Price!=null 
                    ? customers.Order.Price : 0; 

yang lebih verbose dan rumit.


10

Hanya untuk hiburan Anda (mengetahui bahwa Anda semua adalah C # guys ;-).

Saya pikir itu berasal dari Smalltalk, di mana sudah ada selama bertahun-tahun. Di sana didefinisikan sebagai:

di Object:

? anArgument
    ^ self

di UndefinedObject (kelas alias nil):

? anArgument
    ^ anArgument

Ada versi yang mengevaluasi (?) Dan versi yang tidak mengevaluasi (??).
Hal ini sering ditemukan dalam metode getter untuk variabel privat (contoh) malas yang diinisialisasi, yang dibiarkan nol hingga benar-benar diperlukan.


sepertinya membungkus ViewState dengan properti di UserControl. Inisialisasi hanya pada get pertama, jika tidak diatur sebelumnya. =)
Seiti

9

Beberapa contoh di sini tentang mendapatkan nilai menggunakan penggabungan tidak efisien.

Yang benar-benar Anda inginkan adalah:

return _formsAuthWrapper = _formsAuthWrapper ?? new FormsAuthenticationWrapper();

atau

return _formsAuthWrapper ?? (_formsAuthWrapper = new FormsAuthenticationWrapper());

Ini mencegah objek dari diciptakan kembali setiap waktu. Alih-alih variabel pribadi yang tersisa nol dan objek baru dibuat pada setiap permintaan, ini memastikan variabel pribadi ditugaskan jika objek baru dibuat.


Bukankah ??jalan pintas dievaluasi? new FormsAuthenticationWrapper();dievaluasi jika dan hanya jika _formsAuthWrapper nihil.
MSalters

Ya itu intinya. Anda hanya ingin memanggil metode jika variabelnya nol. @MSalters
KingOfHypocrites

8

catatan:

Saya telah membaca seluruh utas ini dan banyak lainnya, tetapi saya tidak dapat menemukan jawaban selengkap ini.

Dengan mana saya benar-benar mengerti "mengapa harus menggunakan ?? dan kapan harus menggunakan ?? dan bagaimana cara menggunakan ??."

Sumber:

Yayasan komunikasi Windows dilepaskan Oleh Craig McMurtry ISBN 0-672-32948-4

Jenis Nilai Nullable

Ada dua keadaan umum di mana seseorang ingin tahu apakah suatu nilai telah ditetapkan untuk sebuah instance dari tipe nilai. Yang pertama adalah ketika instance mewakili nilai dalam database. Dalam kasus seperti itu, seseorang ingin dapat memeriksa contoh untuk memastikan apakah suatu nilai memang ada dalam database. Keadaan lain, yang lebih berkaitan dengan pokok bahasan buku ini, adalah ketika instance mewakili item data yang diterima dari beberapa sumber jarak jauh. Sekali lagi, orang ingin menentukan dari contoh apakah nilai untuk item data itu diterima.

.NET Framework 2.0 menggabungkan definisi tipe generik yang menyediakan untuk kasus-kasus seperti ini di mana seseorang ingin menetapkan nol untuk turunan dari tipe nilai, dan menguji apakah nilai turunannya nol. Definisi tipe generik itu adalah System.Nullable, yang membatasi argumen tipe generik yang dapat diganti dengan T untuk tipe nilai. Contoh tipe yang dibangun dari System.Nullable dapat diberi nilai nol; memang, nilai-nilai mereka adalah null secara default. Dengan demikian, tipe yang dibangun dari System.Nullable dapat disebut sebagai tipe nilai yang dapat dibatalkan. System.Nullable memiliki properti, Value, di mana nilai yang ditetapkan untuk sebuah instance dari tipe yang dibangun darinya dapat diperoleh jika nilai instance tidak nol. Karena itu, seseorang dapat menulis:

System.Nullable<int> myNullableInteger = null;
myNullableInteger = 1;
if (myNullableInteger != null)
{
Console.WriteLine(myNullableInteger.Value);
}

Bahasa pemrograman C # menyediakan sintaks yang disingkat untuk mendeklarasikan tipe yang dibangun dari System.Nullable. Sintaksis itu memungkinkan seseorang untuk menyingkat:

System.Nullable<int> myNullableInteger;

untuk

int? myNullableInteger;

Kompiler akan mencegah seseorang dari mencoba untuk menetapkan nilai tipe nilai nullable ke tipe nilai biasa dengan cara ini:

int? myNullableInteger = null;
int myInteger = myNullableInteger;

Ini mencegah seseorang dari melakukan hal itu karena tipe nilai nullable dapat memiliki nilai null, yang sebenarnya akan terjadi dalam kasus ini, dan nilai itu tidak dapat ditugaskan ke tipe nilai biasa. Meskipun kompiler akan mengizinkan kode ini,

int? myNullableInteger = null;
int myInteger = myNullableInteger.Value;

Pernyataan kedua akan menyebabkan pengecualian untuk dilempar karena setiap upaya untuk mengakses properti System.Nullable.Value adalah operasi yang tidak valid jika tipe yang dibangun dari System.Nullable belum diberi nilai T yang valid, yang belum terjadi dalam hal ini. kasus.

Kesimpulan:

Salah satu cara yang tepat untuk menetapkan nilai tipe nilai nullable ke tipe nilai biasa adalah dengan menggunakan properti System.Nullable.HasValue untuk memastikan apakah nilai T yang valid telah ditetapkan ke tipe nilai nullable:

int? myNullableInteger = null;
if (myNullableInteger.HasValue)
{
int myInteger = myNullableInteger.Value;
}

Opsi lain adalah menggunakan sintaks ini:

int? myNullableInteger = null;
int myInteger = myNullableInteger ?? -1;

Dengan mana integer biasa myInteger diberi nilai integer nullable "myNullableInteger" jika yang terakhir telah diberi nilai integer yang valid; jika tidak, myInteger diberi nilai -1.


1

Ini adalah operator penggabungan nol yang bekerja sama dengan operator ternary.

    a ?? b  => a !=null ? a : b 

Hal lain yang menarik untuk ini adalah, "Tipe yang dapat dibatalkan dapat berisi nilai, atau dapat ditentukan" . Jadi, jika Anda mencoba menetapkan tipe nilai nullable ke tipe nilai non-nullable Anda akan mendapatkan kesalahan waktu kompilasi.

int? x = null; // x is nullable value type
int z = 0; // z is non-nullable value type
z = x; // compile error will be there.

Jadi untuk melakukan itu menggunakan ?? operator:

z = x ?? 1; // with ?? operator there are no issues

1
FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

setara dengan

FormsAuth = formsAuth != null ? formsAuth : new FormsAuthenticationWrapper();

Tapi yang paling keren dari itu adalah Anda bisa memeluk mereka, seperti kata orang lain. Satu yang tidak disentuh adalah bahwa Anda benar-benar dapat menggunakannya untuk melempar pengecualian.

A = A ?? B ?? throw new Exception("A and B are both NULL");

Sungguh luar biasa bahwa Anda memasukkan contoh di pos Anda, meskipun pertanyaannya adalah mencari penjelasan tentang apa yang dilakukan atau tidak dilakukan operator.
TGP1994

1

The ??operator disebut operator null-penggabungan. Ini mengembalikan operan kiri jika operan tidak nol; jika tidak mengembalikan operan kanan.

int? variable1 = null;
int variable2  = variable1 ?? 100;

Setel variable2ke nilai variable1, jika variable1TIDAK nol; jika tidak, jika variable1 == nulldiatur variable2ke 100.


0

Yang lain menggambarkannya dengan Null Coalescing Operatorcukup baik. Bagi yang berminat, ada sintaks singkat di mana ini (pertanyaan SO):

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

setara dengan ini:

FormsAuth ??= new FormsAuthenticationWrapper();

Beberapa merasa lebih mudah dibaca dan ringkas.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.