Mengapa itu Valid untuk Menggabungkan Null Strings tetapi tidak untuk Memanggil "null.ToString ()"?

126

Ini adalah kode C # yang valid

var bob = "abc" + null + null + null + "123";  // abc123

Ini bukan kode C # yang valid

var wtf = null.ToString(); // compiler error

Mengapa pernyataan pertama itu valid?

superlogikal
sumber
97
Saya merasa aneh bahwa Anda null.ToString()diberi nama wtf. Mengapa itu mengejutkan Anda? Anda tidak dapat memanggil metode contoh saat Anda tidak memiliki alasan untuk memanggilnya.
BoltClock
11
@BoltClock: Tentu saja mungkin memanggil metode instance pada instance nol Hanya tidak mungkin dalam C #, tetapi sangat valid pada CLR :)
leppie
1
Jawaban yang berkaitan dengan String.Compat hampir benar. Bahkan, contoh spesifik dalam pertanyaan adalah salah satu pelipatan konstan , dan nol di baris pertama dihilangkan oleh kompiler - yaitu mereka tidak pernah dievaluasi saat runtime karena mereka tidak ada lagi - kompiler telah menghapusnya. Saya menulis sedikit monolog tentang semua aturan untuk penggabungan dan pelipatan konstan dari String di sini untuk info lebih lanjut: stackoverflow.com/questions/9132338/… .
Chris Shain
77
Anda dapat menambahkan apa pun ke string tetapi Anda tidak dapat membuat string dari ketiadaan.
RGB
Apakah pernyataan kedua tidak valid? class null_extension { String ToString( Object this arg ) { return ToString(arg); } }
ctrl-alt-delor

Jawaban:

163

Alasan yang pertama bekerja:

Dari MSDN :

Dalam operasi penggabungan string, kompiler C # memperlakukan string null sama dengan string kosong, tetapi tidak mengonversi nilai string null asli.

Informasi lebih lanjut tentang + operator biner :

Operator biner + melakukan penggabungan string ketika satu atau kedua operan bertipe string.

Jika operan penggabungan string adalah null, string kosong diganti. Jika tidak, argumen non-string apa pun akan dikonversi ke representasi string dengan memanggil ToStringmetode virtual yang diwarisi dari objek tipe.

Jika ToStringkembali null, string kosong diganti.

Alasan kesalahan di detik adalah:

null (C # Referensi) - Kata kunci nol adalah literal yang mewakili referensi nol, yang tidak merujuk ke objek apa pun. null adalah nilai default variabel tipe referensi.

Pranay Rana
sumber
1
Apakah ini akan berhasil? var wtf = ((String)null).ToString();Saya bekerja di Jawa baru-baru ini di mana casting null's mungkin, sudah lama sejak saya bekerja dengan C #.
Jochem
14
@Jochem: Anda masih mencoba memanggil metode pada objek nol, jadi saya kira tidak. Anggap saja sebagai null.ToString()vs ToString(null).
Svish
@Vish ya, sekarang saya pikirkan lagi, itu adalah objek nol, jadi Anda benar, itu tidak akan berfungsi. Ini tidak akan di Jawa juga: pengecualian pointer nol. Lupakan. Tnx untuk balasan Anda! [sunting: mengujinya di Java: NullPointerException. Dengan perbedaan yang dikompilasi oleh cast, tanpa cast tidak].
Jochem
umumnya tidak ada alasan untuk sengaja melakukan concat atau ToString kata kunci null. Jika Anda membutuhkan string kosong gunakan string.Empty. jika Anda perlu memeriksa apakah variabel string adalah nol, Anda dapat menggunakan (myString == null) atau string.IsNullOrEmpty (myString). Atau untuk mengubah variabel string nol menjadi string.Empty gunakan myNewString = (myString == null ?? string.Empty)
csauve
@Jochem casting itu akan membuatnya dikompilasi tetapi memang akan melempar NullReferenceException saat runtime
jeroenh
108

Karena +operator di C # diterjemahkan secara internal String.Concat, yang merupakan metode statis. Dan metode ini terjadi untuk memperlakukan nullseperti string kosong. Jika Anda melihat sumber String.Concatdi Reflector, Anda akan melihatnya:

// while looping through the parameters
strArray[i] = (str == null) ? Empty : str;
// then concatenate that string array

(MSDN menyebutkannya juga: http://msdn.microsoft.com/en-us/library/k9c94ey1.aspx )

Di sisi lain, ToString()apakah metode instance, yang tidak dapat Anda panggil null(jenis apa yang harus digunakan null?).

Botz3000
sumber
2
Tidak ada operator + di kelas String .. Jadi apakah kompiler menerjemahkan ke String.Compat?
superlogis
@superlogical Ya, itulah yang dilakukan oleh kompiler. Jadi sebenarnya, +operator pada string dalam C # hanyalah gula sintaksis untuk String.Concat.
Botz3000
Menambah itu: Kompilator secara otomatis mengambil kelebihan dari Concat yang paling masuk akal. Kelebihan yang tersedia adalah 1, 2 atau 3 parameter objek, 4 parameter objek + __arglistdan paramsversi array objek.
Wormbo
Apa array string yang sedang dimodifikasi di sana? Apakah Concatselalu membuat array baru string non-null bahkan ketika diberi array string non-null, atau ada hal lain yang terjadi?
supercat
@supercat Array yang dimodifikasi adalah array baru, yang kemudian diteruskan ke metode pembantu pribadi. Anda dapat melihat kode sendiri menggunakan reflektor.
Botz3000
29

The sampel pertama akan diterjemahkan menjadi:

var bob = String.Concat("abc123", null, null, null, "abs123");

Itu Concat Metode pemeriksaan input dan menerjemahkan null sebagai string kosong

The sampel kedua akan diterjemahkan menjadi:

var wtf = ((object)null).ToString();

Jadi nullpengecualian referensi akan dihasilkan di sini

Viacheslav Smityukh
sumber
Sebenarnya, AccessViolationExceptionakan dilempar :)
leppie
Saya baru saja :) ((object)null).ToString()=> AccessViolation ((object)1 as string).ToString()=>NullReference
leppie
1
Saya sudah mendapat NullReferenceException. DN 4.0
Viacheslav Smityukh
Menarik. Ketika saya memasukkan ke ((object)null).ToString()dalam try/catchsaya NullReferenceExceptionjuga mendapatkan .
leppie
3
@Ippie kecuali yang tidak membuang AccessViolation (mengapa?) - NullReferenceException di sini.
jeroenh
11

Bagian pertama dari kode Anda diperlakukan seperti itu di String.Concat,

yang disebut oleh kompilator C # saat Anda menambahkan string. " abc" + nullditerjemahkan ke String.Concat("abc", null),

dan secara internal, metode itu diganti nulldengan String.Empty. Jadi, itu sebabnya bagian pertama kode Anda tidak membuang pengecualian. itu seperti

var bob = "abc" + string.Empty + string.Empty + string.Empty + "123";  //abc123

Dan di bagian ke-2 dari kode Anda melemparkan pengecualian karena 'nol' bukan objek, kata kunci nol adalah literal yang mewakili referensi nol , yang tidak merujuk ke objek apa pun. null adalah nilai default variabel tipe referensi.

Dan ' ToString()' adalah metode yang dapat dipanggil oleh instance dari suatu objek tetapi tidak secara literal.

Talha
sumber
3
String.Emptytidak sama dengan null. Itu hanya diperlakukan dengan cara yang sama dalam beberapa metode seperti String.Concat. Misalnya, jika Anda memiliki variabel string diatur ke null, C # tidak akan menggantinya dengan String.Emptyjika Anda mencoba memanggil metode di dalamnya.
Botz3000
3
Hampir. nulltidak diperlakukan seperti String.Emptyoleh C #, itu hanya diperlakukan seperti itu di String.Concat, yang adalah apa yang disebut kompiler C # ketika Anda menambahkan string. "abc" + nullditerjemahkan ke String.Concat("abc", null), dan secara internal, metode itu diganti nulldengan String.Empty. Bagian kedua sepenuhnya benar.
Botz3000
9

Dalam kerangka COM yang mendahului. Net, itu perlu untuk setiap rutin yang menerima string untuk membebaskannya ketika sudah selesai dengan itu. Karena itu sangat umum untuk string kosong untuk masuk dan keluar dari rutinitas, dan karena berusaha untuk "membebaskan" pointer nol didefinisikan sebagai operasi tidak melakukan apa-apa yang sah, Microsoft memutuskan untuk memiliki string pointer kosong mewakili string kosong.

Untuk memungkinkan kompatibilitas dengan COM, banyak rutin di .net akan menafsirkan objek nol sebagai representasi hukum sebagai string kosong. Dengan beberapa perubahan kecil .net dan bahasanya (yang paling memungkinkan anggota contoh untuk menunjukkan "jangan dipanggil sebagai virtual"), Microsoft dapat membuat nullobjek dengan tipe String yang dinyatakan berperilaku lebih seperti string kosong. Jika Microsoft telah melakukan itu, itu juga harus membuat Nullable<T>pekerjaan agak berbeda (sehingga memungkinkan - Nullable<String>sesuatu yang seharusnya mereka lakukan IMHO) dan / atau menentukan NullableStringjenis yang sebagian besar akan dipertukarkan dengan String, tetapi yang tidak akan menganggap nullsebagai string kosong yang valid.

Seperti adanya, ada beberapa konteks di mana a nullakan dianggap sebagai string kosong yang sah dan yang lainnya tidak. Bukan situasi yang sangat membantu, tetapi yang harus diperhatikan oleh programmer. Secara umum, ekspresi formulir stringValue.someMemberakan gagal jika stringValueada null, tetapi sebagian besar metode kerangka kerja dan operator yang menerima string sebagai parameter akan dianggap nullsebagai string kosong.

supercat
sumber
5

'+'adalah operator infiks. Seperti halnya operator apa pun, itu benar-benar memanggil metode. Anda dapat membayangkan versi non-infiks"wow".Plus(null) == "wow"

Pelaksana telah memutuskan sesuatu seperti ini ...

class String
{
  ...
  String Plus(ending)
  {
     if(ending == null) return this;
     ...
  }
} 

Jadi .. teladan Anda menjadi

var bob = "abc".Plus(null).Plus(null).Plus(null).Plus("123");  // abc123

yang sama dengan

var bob = "abc".Plus("123");  // abc123

Tanpa titik tidak null menjadi string. Jadi null.ToString()tidak ada bedanya null.VoteMyAnswer(). ;)

Nigel Thorne
sumber
Sungguh, ini lebih seperti var bob = Plus(Plus(Plus(Plus("abc",null),null),null),"123");. Kelebihan operator adalah metode statis di hati mereka: msdn.microsoft.com/en-us/library/s53ehcz3(v=VS.71).aspx Jika tidak, var bob = null + "abc";atau khususnya string bob = null + null;tidak akan valid.
Ben Mosher
Anda benar, saya hanya merasa saya akan terlalu membingungkan jika saya menjelaskannya seperti itu.
Nigel Thorne
3

Saya kira karena itu literal yang tidak merujuk ke objek apa pun. ToString()membutuhkan sebuah object.

Saeed Neamati
sumber
3

Seseorang berkata di utas diskusi ini bahwa Anda tidak dapat membuat string dari ketiadaan. (yang merupakan ungkapan yang bagus seperti yang saya pikirkan). Tapi ya - Anda dapat :-), seperti yang ditunjukkan contoh berikut:

var x = null + (string)null;     
var wtf = x.ToString();

berfungsi dengan baik dan tidak membuang pengecualian sama sekali. Satu-satunya perbedaan adalah bahwa Anda perlu membuang salah satu nulls ke dalam string - jika Anda menghapus cast (string) , maka contohnya masih mengkompilasi, tetapi melempar pengecualian run-time: "Operator '+' ambigu pada operan dari ketik '<null>' dan '<null>' ".

NB Dalam contoh kode di atas, nilai x tidak nol seperti yang Anda harapkan, itu sebenarnya adalah string kosong setelah Anda memasukkan salah satu operan ke dalam string.


Fakta lain yang menarik adalah bahwa dalam C # /. NET cara nulldiperlakukan tidak selalu sama jika Anda menganggap tipe data yang berbeda. Sebagai contoh:

int? x = 1;  //  string x = "1";
x = x + null + null;
Console.WriteLine((x==null) ? "<null>" : x.ToString());

Mengenai baris pertama dari cuplikan kode: If xadalah variabel integer yang dapat dibatalkan (misint? ) yang berisi nilai 1, maka Anda mendapatkan hasilnya <null>kembali. Jika itu adalah string (seperti yang ditunjukkan dalam komentar) dengan nilai "1", maka Anda akan "1"kembali daripada <null>.

NB Juga menarik: Jika Anda menggunakan var x = 1;untuk baris pertama, maka Anda mendapatkan kesalahan runtime. Mengapa? Karena tugas akan mengubah variabel xmenjadi tipe data int, yang tidak dapat dibatalkan. Compiler tidak berasumsi di int?sini, dan karenanya gagal di baris ke-2 di mananull ditambahkan.

Mat
sumber
2

Menambahkan nullke string diabaikan saja. null(dalam contoh kedua Anda) bukan turunan dari objek apa pun, jadi ia bahkan tidak memiliki ToString()metode. Itu hanya literal.

Thorsten Dittmar
sumber
1

Karena tidak ada perbedaan antara string.Emptydan nullketika Anda memainkan string. Anda dapat melewatkan null string.Formatjuga. Tetapi Anda mencoba memanggil metode null, yang akan selalu menghasilkan a NullReferenceExceptiondan karenanya menghasilkan kesalahan kompiler.
Jika karena alasan tertentu Anda benar-benar ingin melakukannya, Anda dapat menulis metode ekstensi, yang memeriksa nulldan kemudian kembali string.Empty. Tetapi ekstensi seperti itu seharusnya hanya digunakan jika benar-benar diperlukan (menurut saya).

Franky
sumber
0

Secara umum: Ini mungkin atau mungkin tidak valid menerima null sebagai parameter tergantung pada spesifikasi, tetapi selalu tidak valid untuk memanggil metode pada null.


Itu dan topik lain mengapa operan + operator bisa nol jika ada string. Ini adalah hal yang agak VB (maaf guys) untuk membuat hidup programmer lebih mudah, atau seandainya programmer tidak dapat menangani nulls. Saya sepenuhnya tidak setuju dengan spesifikasi ini. 'tidak diketahui' + 'apapun' harus tetap 'tidak diketahui' ...

g.pickardou
sumber