Mengapa ekspresi berikut berbeda?
[1] (object)0 == (object)0 //false
[2] ((object)0).Equals((object)0) // true
Sebenarnya, saya benar-benar dapat memahami [1] karena mungkin runtime NET. Akan box
integer dan mulai membandingkan referensi sebagai gantinya. Tetapi mengapa [2] berbeda?
short myShort = 0; int myInt = 0; Console.WriteLine("{0}{1}{2}", myShort.Equals(myInt), myInt.Equals(myShort), myInt == myShort);
Sekarang periksa dengan kenyataan. Apakah prediksi Anda benar? Jika tidak, dapatkah Anda menjelaskan perbedaannya?int16
akashort
Equals, lalu lihat msdn.microsoft.com/en-us/library/ms173105.aspx . Saya tidak ingin merusak teka-teki Eric Lippert, tetapi seharusnya cukup mudah untuk mengetahuinya setelah Anda membaca halaman-halaman itu.((Integer)0)==((Integer)0)
mengevaluasi ke true.IFormattable x = 0; bool test = (object)x == (object)x;
. Tidak ada tinju baru yang dilakukan saat struct sudah ada di dalam kotak.Jawaban:
Alasan panggilan berperilaku berbeda adalah karena panggilan tersebut terikat ke metode yang sangat berbeda.
The
==
kasus akan mengikat operator kesetaraan referensi statis. Ada 2 kotak independenint
nilai dibuat karena itu bukan referensi yang sama.Dalam kasus kedua, Anda mengikat ke metode instance
Object.Equals
. Ini adalah metode virtual yang akan memfilterInt32.Equals
dan ini memeriksa bilangan bulat dalam kotak. Kedua nilai integer adalah 0 sehingga keduanya samasumber
==
kasus tidak meneleponObject.ReferenceEquals
. Ini hanya menghasilkanceq
instruksi IL untuk melakukan perbandingan referensi.==
lakukan hal yang sama sepertiReferenceEquals
(pada Object). Itu semua hanya pengoptimalan internal di sisi MS untuk menghindari panggilan fungsi internal yang tidak perlu pada fungsi yang sering digunakan.bool operator ==(object x, object y);
bool operator !=(object x, object y);
Operator mengembalikan hasil dari membandingkan dua referensi untuk persamaan atau non-persamaan. Metode tersebut tidak diwajibkanSystem.Object.ReferenceEquals
untuk menentukan hasil. Kepada @markmnl: Tidak, kompilator C # tidak sebaris, itu adalah sesuatu yang kadang-kadang dilakukan oleh jitter (tetapi tidak dalam kasus ini). Jadi 280Z28 benarReferenceEquals
metode ini sebenarnya tidak digunakan.Cat Whiskers; Dog Fido; IDog Fred;
(untuk antarmuka non-terkaitICat
danIDog
dan kelas non-terkaitCat:ICat
danDog:IDog
), perbandinganWhiskers==Fido
danWhiskers==34
akan menjadi hukum (yang pertama hanya bisa menjadi benar jika Kumis dan Fido berdua null; yang kedua tidak pernah bisa benar ). Nyatanya, kompilator C # akan menolak keduanya.Whiskers==Fred;
akan dilarang jikaCat
disegel, tetapi diizinkan jika tidak.Saat Anda memasukkan nilai int
0
(atau tipe nilai lainnya) keobject
, nilainya dikotakkan . Setiap castobject
menghasilkan kotak yang berbeda (contoh objek yang berbeda). The==
Operator untukobject
jenis melakukan referensi perbandingan, sehingga ia mengembalikan palsu karena sisi kiri dan sisi kanan tidak contoh yang sama.Di sisi lain, saat Anda menggunakan
Equals
, yang merupakan metode virtual, ia menggunakan implementasi tipe kotak yang sebenarnya, yaituInt32.Equals
, yang mengembalikan nilai true karena kedua objek memiliki nilai yang sama.sumber
The
==
operator, menjadi statis, tidak virtual. Ini akan menjalankan kode persis seperti ituobject
didefinisikan oleh kelas (`objek menjadi jenis waktu kompilasi dari operan), yang akan melakukan perbandingan referensi, terlepas dari jenis waktu proses dari salah satu objek.The
Equals
metode adalah metode contoh virtual. Ini akan menjalankan kode yang ditentukan dalam jenis run-time sebenarnya dari objek (pertama), bukan kode diobject
kelas. Dalam hal ini, objeknya adalah anint
, sehingga akan melakukan perbandingan nilai, karena itulah yangint
didefinisikan oleh tipe untukEquals
metodenya.sumber
==
Token sebenarnya merupakan dua operator, salah satunya adalah overloadable dan salah satu yang tidak. Perilaku operator kedua sangat berbeda dengan perilaku overload (objek, objek).The
Equals()
metode adalah virtual.Oleh karena itu, itu selalu memanggil implementasi konkret, bahkan ketika callsite dicor
object
.int
menimpaEquals()
untuk membandingkan berdasarkan nilai, sehingga Anda mendapatkan perbandingan nilai.sumber
==
Menggunakan:Object.ReferenceEquals
Object.Equals
membandingkan nilainya.Itu
object.ReferenceEquals
metode membandingkan referensi. Saat mengalokasikan objek, Anda menerima referensi berisi nilai yang menunjukkan lokasi memorinya selain data objek di heap memori.The
object.Equals
metode membandingkan isi objek. Ini pertama kali memeriksa apakah referensinya sama, seperti halnya object.ReferenceEquals. Tapi kemudian memanggil ke dalam metode Equals turunan untuk menguji kesetaraan lebih lanjut. Lihat ini:sumber
Object.ReferenceEquals
berperilaku seperti metode yang menggunakan==
operator C # pada operannya, operator operator persamaan referensi C # (yang direpresentasikan dengan menggunakan==
jenis operan yang tidak memiliki kelebihan beban yang ditentukan) menggunakan instruksi khusus daripada memanggilReferenceEquals
. Selanjutnya,Object.ReferenceEquals
akan menerima operan yang hanya bisa cocok jika keduanya kebetulan nol, dan akan menerima operan yang perlu dipaksakan tipeObject
dan dengan demikian tidak mungkin cocok dengan apa pun, sementara versi referensi-kesetaraan==
akan menolak untuk mengkompilasi penggunaan tersebut .Operator C # menggunakan token
==
untuk mewakili dua operator yang berbeda: operator perbandingan yang dapat kelebihan beban secara statis dan operator perbandingan referensi yang tidak dapat dilebihkan. Saat menemukan==
token, pertama kali memeriksa untuk melihat apakah ada kelebihan pengujian kesetaraan yang berlaku untuk jenis operan. Jika demikian, itu akan memicu kelebihan itu. Jika tidak, ini akan memeriksa apakah tipe tersebut berlaku untuk operator perbandingan referensi. Jika demikian, itu akan menggunakan operator itu. Jika tidak ada operator yang dapat diterapkan ke jenis operan, kompilasi akan gagal.Kode
(Object)0
tersebut tidak hanya memuatInt32
keObject
:Int32
, seperti semua jenis nilai, benar-benar mewakili dua jenis, salah satu yang menggambarkan nilai-nilai dan lokasi penyimpanan (seperti nol literal), tetapi tidak berasal dari apa-apa, dan salah satu yang menggambarkan benda tumpukan dan berasal dariObject
; karena hanya tipe terakhir yang mungkin upcastObject
, compiler harus membuat objek heap baru dari tipe yang terakhir tersebut. Setiap pemanggilan(Object)0
membuat objek heap baru, sehingga dua operan==
adalah objek yang berbeda, yang masing-masing, secara independen, merangkumInt32
nilai 0.Kelas
Object
tidak memiliki kelebihan beban yang dapat digunakan yang ditentukan untuk operator yang sama. Akibatnya, kompilator tidak akan dapat menggunakan operator uji persamaan yang kelebihan beban, dan akan kembali menggunakan uji persamaan referensi. Karena dua operan==
merujuk ke objek yang berbeda, itu akan melaporkanfalse
. Perbandingan kedua berhasil karena menanyakan satu instance objek heapInt32
apakah sama dengan yang lain. Karena contoh itu tahu apa artinya sama dengan contoh berbeda lainnya, dia bisa menjawabtrue
.sumber
0
dalam kode Anda, saya berasumsi itu membuat objek int di heap untuk itu. Ini bukan referensi unik untuk satu nilai nol statis global (seperti bagaimana mereka membuat String.Empty untuk menghindari membuat objek string kosong baru hanya untuk menginisialisasi string baru) Jadi saya cukup yakin bahwa bahkan melakukan a0.ReferenceEquals(0)
akan mengembalikan false, karena kedua 0 adalah objek yang baru dibuatInt32
.0.ReferenceEquals(0)
Akan gagal karena Anda mencoba memanggil metode pada konstanta waktu kompilasi. tidak ada objek untuk menggantungnya. Int unboxed adalah struct, disimpan di stack. Bahkanint i = 0; i.ReferenceEquals(...)
tidak akan berhasil. KarenaSystem.Int32
TIDAK mewarisi dariObject
.System.Int32
adalahstruct
,struct
adalahSystem.ValueType
, yang mewarisi dirinya sendiriSystem.Object
. Itulah mengapa adaToString()
metode danEquals
metode untukSystem.Int32
Object
, dan di sanalah saya berhenti mengkhawatirkannya sejak lama. Tidak perlu melangkah lebih jauh. AFAIK CLR menangani dua jenis secara berbeda, dan secara khusus. Bukan hanya warisan. Iniis
salah satu dari dua jenis data. Saya hanya tidak ingin ada orang yang membaca komentar itu dan keluar jalur, termasuk keanehan tentang string kosong yang mengabaikan proses internal string.Kedua pemeriksaan tersebut berbeda. Yang pertama memeriksa identitas , yang kedua untuk kesetaraan . Secara umum, dua istilah identik, jika merujuk pada objek yang sama. Ini menyiratkan bahwa mereka setara. Dua suku sama, jika nilainya sama.
Dalam istilah identitas pemrograman biasanya dirusak oleh persamaan referensi. Jika penunjuk ke kedua suku sama (!), Maka obyek yang mereka tunjuk sama persis. Namun, jika penunjuknya berbeda, nilai objek yang dituju masih bisa sama. Dalam C # identitas dapat diperiksa dengan menggunakan
Object.ReferenceEquals
anggota statis , sedangkan kesetaraan diperiksa menggunakan anggota non-statisObject.Equals
. Karena Anda pengecoran dua bilangan bulat ke objek (yang disebut "tinju", btw), yang operatior==
dariobject
melakukan cek pertama, yang secara default dipetakan keObject.ReferenceEquals
dan memeriksa identitas. Jika Anda secara eksplisit memanggilEquals
-member non-statis , pengiriman dinamis menghasilkan panggilan keInt32.Equals
, yang memeriksa kesetaraan.Kedua konsep tersebut serupa, tetapi tidak sama. Awalnya mungkin tampak membingungkan, tetapi perbedaan kecil itu sangat penting! Bayangkan dua orang, yaitu "Alice" dan "Bob". Mereka berdua tinggal di rumah kuning. Berdasarkan asumsi, Alice dan Bob tinggal di sebuah distrik yang rumahnya hanya berbeda warna, keduanya bisa tinggal di rumah kuning yang berbeda. Jika Anda membandingkan kedua rumah, Anda akan mengenali, bahwa keduanya sama sekali, karena keduanya berwarna kuning! Namun, mereka tidak berbagi rumah yang sama sehingga rumah mereka sama , tetapi tidak identik . Identitas menyiratkan bahwa mereka tinggal di rumah yang sama .
Catatan : beberapa bahasa menentukan
===
operator untuk memeriksa identitas.sumber