Bagaimana null + true sebuah string?

112

Karena truebukan tipe string, bagaimana null + truestring itu?

string s = true;  //Cannot implicitly convert type 'bool' to 'string'   
bool b = null + true; //Cannot implicitly convert type 'string' to 'bool'

apa alasan dibalik ini?

Javed Akram
sumber
27
Anda melakukan sesuatu yang tidak masuk akal dan kemudian tidak menyukai pesan yang dihasilkan kompilator? Ini adalah kode C # yang tidak valid ... apa yang sebenarnya ingin Anda lakukan?
Hogan
19
@ Hogan: Kode tersebut tampaknya acak dan cukup akademis untuk pertanyaan yang diajukan karena rasa ingin tahu. Tebakan saya ...
BoltClock
8
null + true mengembalikan 1 di JavaScript.
Fatih Acet

Jawaban:

147

Anehnya, ini hanya mengikuti aturan dari spesifikasi bahasa C #.

Dari bagian 7.3.4:

Operasi dalam bentuk x op y, di mana op adalah operator biner yang dapat kelebihan beban, x adalah ekspresi tipe X, dan y adalah ekspresi tipe Y, diproses sebagai berikut:

  • Himpunan calon operator yang ditentukan pengguna yang disediakan oleh X dan Y untuk operator operasi op (x, y) ditentukan. Himpunan ini terdiri dari gabungan operator kandidat yang disediakan oleh X dan operator kandidat yang disediakan oleh Y, masing-masing ditentukan menggunakan aturan §7.3.5. Jika X dan Y adalah tipe yang sama, atau jika X dan Y diturunkan dari tipe basis yang sama, maka operator kandidat bersama hanya terjadi dalam kumpulan gabungan satu kali.
  • Jika kumpulan calon operator yang ditentukan pengguna tidak kosong, maka ini menjadi kumpulan calon operator untuk operasi. Jika tidak, implementasi op operator biner yang telah ditentukan, termasuk bentuk yang diangkat, menjadi kumpulan calon operator untuk operasi tersebut. Implementasi yang telah ditentukan sebelumnya dari operator tertentu ditentukan dalam deskripsi operator (§7.8 hingga §7.12).
  • Aturan resolusi kelebihan beban pada §7.5.3 diterapkan ke kumpulan operator kandidat untuk memilih operator terbaik sehubungan dengan daftar argumen (x, y), dan operator ini menjadi hasil dari proses resolusi kelebihan beban. Jika resolusi kelebihan beban gagal memilih satu operator terbaik, kesalahan waktu pengikatan terjadi.

Jadi, mari kita bahas ini secara bergantian.

X adalah tipe nol di sini - atau bukan tipe sama sekali, jika Anda ingin menganggapnya seperti itu. Itu tidak memberikan kandidat apa pun. Y adalah bool, yang tidak menyediakan +operator yang ditentukan pengguna . Jadi langkah pertama tidak menemukan operator yang ditentukan pengguna.

Kompiler kemudian beralih ke poin poin kedua, melihat melalui implementasi + operator biner yang telah ditentukan dan formulir yang diangkat. Ini tercantum di bagian 7.8.4 spesifikasi.

Jika Anda melihat melalui operator yang telah ditentukan sebelumnya, satu - satunya yang berlaku adalah string operator +(string x, object y). Jadi set kandidat memiliki satu entri. Itu membuat poin terakhir sangat sederhana ... resolusi kelebihan beban memilih operator itu, memberikan tipe ekspresi keseluruhan string.

Satu hal yang menarik adalah hal ini akan terjadi meskipun ada operator yang ditentukan pengguna lain yang tersedia pada jenis yang tidak disebutkan. Sebagai contoh:

// Foo defined Foo operator+(Foo foo, bool b)
Foo f = null;
Foo g = f + true;

Tidak apa-apa, tetapi tidak digunakan untuk literal nol, karena kompilator tidak tahu untuk mencarinya Foo. Itu hanya tahu untuk dipertimbangkan stringkarena ini adalah operator yang ditentukan sebelumnya secara eksplisit tercantum dalam spesifikasi. (Sebenarnya, ini bukan operator yang ditentukan oleh tipe string ... 1 ) Itu berarti bahwa ini akan gagal untuk dikompilasi:

// Error: Cannot implicitly convert type 'string' to 'Foo'
Foo f = null + true;

Jenis operan kedua lainnya akan menggunakan beberapa operator lain, tentu saja:

var x = null + 0; // x is Nullable<int>
var y = null + 0L; // y is Nullable<long>
var z = null + DayOfWeek.Sunday; // z is Nullable<DayOfWeek>

1 Anda mungkin bertanya - tanya mengapa tidak ada operator + string. Ini pertanyaan yang masuk akal, dan saya hanya menebak-nebak jawabannya, tetapi pertimbangkan ungkapan ini:

string x = a + b + c + d;

Jika stringtidak ada casing khusus di kompiler C #, ini akan berakhir secara efektif:

string tmp0 = (a + b);
string tmp1 = tmp0 + c;
string x = tmp1 + d;

Jadi itu menciptakan dua string perantara yang tidak perlu. Namun, karena ada dukungan khusus di dalam kompilator, ia sebenarnya dapat mengkompilasi di atas sebagai:

string x = string.Concat(a, b, c, d);

yang dapat membuat hanya satu string dengan panjang yang tepat, menyalin semua data tepat satu kali. Bagus.

Jon Skeet
sumber
'memberikan tipe ekspresi keseluruhan string' Saya tidak setuju. Kesalahannya berasal dari truetidak dapat dikonversi ke string. Jika ekspresi itu valid, jenisnya akan string, tetapi dalam kasus ini kegagalan untuk mengonversi ke string membuat seluruh ekspresi menjadi error, dan karenanya tidak memiliki tipe.
leppie
6
@leppie: Ekspresi ini valid, dan tipenya adalah string. Coba "var x = null + true;" - itu mengkompilasi dan xbertipe string. Perhatikan bahwa tanda tangan yang digunakan di sini adalah string operator+(string, object)- itu diubah boolmenjadi object(yang bagus), bukan menjadi string.
Jon Skeet
Terima kasih Jon, mengerti sekarang. BTW bagian 14.7.4 dalam versi 2 spesifikasi.
leppie
@leppie: Apakah itu ada dalam penomoran ECMA? Itu selalu sangat berbeda :(
Jon Skeet
47
dalam 20 menit. Dia menulis semuanya dalam 20 menit. Dia harus menulis buku atau sesuatu ... oh tunggu.
Epaga
44

Alasannya adalah karena begitu Anda memperkenalkan +aturan pengikatan operator C # mulai berlaku. Ini akan mempertimbangkan kumpulan +operator yang tersedia dan memilih kelebihan beban terbaik. Salah satu operator tersebut adalah sebagai berikut

string operator +(string x, object y)

Kelebihan beban ini kompatibel dengan tipe argumen dalam ekspresi null + true. Oleh karena itu dipilih sebagai operator dan dievaluasi sebagai dasarnya ((string)null) + trueyang mengevaluasi nilai "True".

Bagian 7.7.4 dari spesifikasi bahasa C # berisi rincian seputar resolusi ini.

JaredPar
sumber
1
Saya perhatikan IL yang dihasilkan langsung memanggil Concat. Saya pikir saya akan melihat panggilan ke fungsi + operator di sana. Apakah itu hanya pengoptimalan sebaris?
Quentin-starin
1
@qstarin saya percaya alasannya adalah bahwa tidak ada benar-benar sebuah operator+untuk string. Alih-alih itu hanya ada dalam pikiran kompiler dan itu hanya menerjemahkannya menjadi panggilan untukstring.Concat
JaredPar
1
@qstarin @JaredPar: Saya membahasnya lebih dalam dalam jawaban saya.
Jon Skeet
11

Kompilator keluar mencari operator + () yang bisa mengambil argumen null terlebih dahulu. Tak satu pun dari tipe nilai standar yang memenuhi syarat, null bukanlah nilai yang valid untuk mereka. Satu-satunya kecocokan adalah System.String.operator + (), tidak ada ambiguitas.

Argumen ke-2 dari operator itu juga berupa string. Itu tidak berguna, tidak bisa secara implisit mengubah bool menjadi string.

Hans Passant
sumber
10

Menariknya, menggunakan Reflector untuk memeriksa apa yang dihasilkan, kode berikut:

string b = null + true;
Console.WriteLine(b);

diubah menjadi ini oleh kompilator:

Console.WriteLine(true);

Alasan di balik "pengoptimalan" ini agak aneh yang harus saya katakan, dan tidak sesuai dengan pilihan operator yang saya harapkan.

Juga, kode berikut ini:

var b = null + true; 
var sb = new StringBuilder(b);

diubah menjadi

string b = true; 
StringBuilder sb = new StringBuilder(b);

dimana string b = true;sebenarnya tidak diterima oleh kompilator.

Peter Lillevold
sumber
8

nullakan dilemparkan ke string null, dan ada konverter implisit dari bool ke string sehingga trueakan dilemparkan ke string dan kemudian, +operator akan diterapkan: seperti: string str = "" + true.ToString ();

jika Anda memeriksanya dengan Ildasm:

string str = null + true;

itu seperti di bawah ini:

.locals init ([0] string str)
  IL_0000:  nop
  IL_0001:  ldc.i4.1
  IL_0002:  box        [mscorlib]System.Boolean
  IL_0007:  call       string [mscorlib]System.String::Concat(object)
  IL_000c:  stloc.0
Saeed Amiri
sumber
5
var b = (null + DateTime.Now); // String
var b = (null + 1);            // System.Nullable<Int32> | same with System.Single, System.Double, System.Decimal, System.TimeSpan etc
var b = (null + new Object()); // String | same with any ref type

Gila?? Tidak, pasti ada alasan di baliknya.

Seseorang menelepon Eric Lippert...

deklon
sumber
5

Alasannya adalah kemudahan (merangkai string adalah tugas yang umum).

Seperti yang dikatakan BoltClock, operator '+' didefinisikan pada tipe numerik, string, dan juga dapat didefinisikan untuk tipe kita sendiri (operator overloading).

Jika tidak ada operator '+' yang kelebihan beban pada tipe argumen dan bukan tipe numerik, kompilator defaultnya adalah penggabungan string.

Kompilator menyisipkan panggilan ke String.Concat(...)saat Anda menggabungkan menggunakan '+', dan implementasi Concat memanggil ToString pada setiap objek yang diteruskan ke dalamnya.

quentin-starin
sumber