Jawaban singkat:
Tidak ada instruksi "bandingkan-tidak-sama" dalam IL, sehingga !=
operator C # tidak memiliki korespondensi yang tepat dan tidak dapat diterjemahkan secara harfiah.
Namun ada instruksi "bandingkan-sama" ( ceq
, korespondensi langsung ke ==
operator), jadi dalam kasus umum, x != y
diterjemahkan seperti padanan yang sedikit lebih panjang (x == y) == false
.
Ada juga instruksi "bandingkan-lebih-dari-dalam" di IL ( cgt
) yang memungkinkan kompiler mengambil jalan pintas tertentu (yaitu menghasilkan kode IL lebih pendek), salah satunya adalah bahwa perbandingan ketidaksetaraan objek terhadap nol,, obj != null
diterjemahkan seolah-olah mereka " obj > null
".
Mari kita bahas lebih detail.
Jika tidak ada instruksi "bandingkan-tidak-sama" dalam IL, lalu bagaimana metode berikut akan diterjemahkan oleh kompiler?
static bool IsNotEqual(int x, int y)
{
return x != y;
}
Seperti yang sudah dikatakan di atas, kompiler akan mengubahnya x != y
menjadi (x == y) == false
:
.method private hidebysig static bool IsNotEqual(int32 x, int32 y) cil managed
{
ldarg.0 // x
ldarg.1 // y
ceq
ldc.i4.0 // false
ceq // (note: two comparisons in total)
ret
}
Ternyata kompiler tidak selalu menghasilkan pola yang bertele-tele. Mari kita lihat apa yang terjadi ketika kita mengganti y
dengan konstanta 0:
static bool IsNotZero(int x)
{
return x != 0;
}
IL yang diproduksi agak lebih pendek daripada dalam kasus umum:
.method private hidebysig static bool IsNotZero(int32 x) cil managed
{
ldarg.0 // x
ldc.i4.0 // 0
cgt.un // (note: just one comparison)
ret
}
Kompilator dapat mengambil keuntungan dari fakta bahwa bilangan bulat yang ditandatangani disimpan dalam komplemen dua (di mana, jika pola bit yang dihasilkan ditafsirkan sebagai bilangan bulat yang tidak ditandatangani - itulah .un
artinya - 0 memiliki nilai sekecil mungkin), sehingga menerjemahkan x == 0
seolah-olah itu adalah unchecked((uint)x) > 0
.
Ternyata kompiler dapat melakukan hal yang sama untuk pemeriksaan ketimpangan terhadap null
:
static bool IsNotNull(object obj)
{
return obj != null;
}
Kompiler menghasilkan IL yang hampir sama dengan untuk IsNotZero
:
.method private hidebysig static bool IsNotNull(object obj) cil managed
{
ldarg.0
ldnull // (note: this is the only difference)
cgt.un
ret
}
Rupanya, kompiler diperbolehkan untuk mengasumsikan bahwa pola bit dari null
referensi adalah pola bit terkecil yang mungkin untuk referensi objek apa pun.
Pintasan ini secara eksplisit disebutkan dalam Common Language Infrastructure Annotated Standard (edisi pertama dari Oktober 2003) (di halaman 491, sebagai catatan kaki pada Tabel 6-4, "Perbandingan Biner atau Operasi Cabang"):
" cgt.un
diperbolehkan dan dapat diverifikasi pada ObjectRefs (O). Ini biasanya digunakan ketika membandingkan ObjectRef dengan nol (tidak ada instruksi" bandingkan-tidak-sama ", yang kalau tidak akan menjadi solusi yang lebih jelas)."
int
kisaran memiliki representasi yang samaint
seperti ketika mereka masukuint
. Itu persyaratan yang jauh lebih lemah daripada komplemen dua.int
telah diambil dengan nilai yang sama diuint
, jadi semua representasi yang sesuai dengan nilai negatifint
harus sesuai dengan beberapa nilaiuint
lebih besar dari0x7FFFFFFF
, tetapi tidak terlalu penting nilai mana yang adalah. (Sebenarnya, semua yang benar-benar diperlukan adalah nol diwakili dengan cara yang sama di keduaint
danuint
.)cgt.un
memperlakukanint
sebagaiuint
tanpa mengubah pola bit yang mendasarinya. (Bayangkan bahwacgt.un
pertama akan mencoba untuk underflows memperbaiki dengan memetakan semua angka negatif ke 0. Dalam kasus Anda jelas tidak bisa menggantikan> 0
untuk!= 0
.)>
dapat diverifikasi IL. Dengan begitu orang dapat membandingkan dua objek non-null dan mendapatkan hasil boolean (yang non-deterministik). Itu bukan masalah keamanan memori tetapi rasanya seperti desain kotor yang tidak dalam semangat umum kode yang dikelola dengan aman. Desain ini membocorkan fakta bahwa referensi objek diimplementasikan sebagai pointer. Tampak seperti cacat desain. NET CLI.ldnull
,initobj
dannewobj
). Jadi penggunaancgt.un
untuk membandingkan referensi objek terhadap referensi nol tampaknya bertentangan dengan bagian III.1.1.4 dalam lebih dari satu cara.