Mengapa C # gagal membandingkan dua tipe objek satu sama lain tetapi VB tidak?

152

Saya memiliki dua objek di C # dan tidak tahu apakah itu Boolean atau tipe lainnya. Namun ketika saya mencoba membandingkan C # gagal memberikan jawaban yang benar. Saya telah mencoba kode yang sama dengan VB.NET dan berhasil!

Adakah yang bisa memberitahu saya cara memperbaikinya jika ada solusi?

C #:

object a = true;
object b = true;
object c = false;
if (a == b) c = true;
MessageBox.Show(c.ToString()); //Outputs False !!

VB.NET:

Dim a As Object = True
Dim b As Object = True
Dim c As Object = False
If (a = b) Then c = True
MessageBox.Show(c.ToString()) '// Outputs True
Mohsen Sarkar
sumber
3
bagaimana jika Anda mengubah pembanding kesetaraan a.Equals(b)?
Jason Meckley
8
Ini adalah pertanyaan yang bagus untuk tujuan pedagogis.
Lobo
10
Karena kode VB.NET Anda tidak sama dengan kode C # Anda.
Security Hound
9
Ketika Anda menetapkan untuk aAnda mendapatkan tinju dan membuat sebuah kotak yang berisi true. Ketika Anda menetapkan untuk bAnda dapatkan kotak lain juga mengandung true. Ketika Anda membandingkan adan b, karena keduanya bertipe waktu kompilasi object, Anda memanggil kelebihan yang operator ==(object, object)ditentukan oleh Spesifikasi Bahasa C #. Kelebihan ini memeriksa untuk melihat apakah referensi pergi ke objek yang sama. Karena Anda memiliki dua kotak, hasilnya adalah false, dan pernyataan "di bawah" Anda iftidak akan berjalan. Untuk memahami ini lebih baik, cobalah untuk mengubah penugasan buntuk ini: object b = a;Sekarang Anda hanya memiliki satu kotak.
Jeppe Stig Nielsen
3
Saya memiliki kesempatan sebelumnya untuk mengatakan "Berhati-hatilah dengan asumsi bahwa VB.NET dan C # adalah bahasa yang sama yang diucapkan dengan aksen yang berbeda - mereka tidak"
AakashM

Jawaban:

168

Dalam C #, ==operator (ketika diterapkan pada ekspresi tipe referensi) melakukan pemeriksaan kesetaraan referensi kecuali itu kelebihan beban . Anda membandingkan dua referensi yang merupakan hasil konversi tinju, jadi itu adalah referensi yang berbeda.

Suntingan: Dengan tipe yang overload ==, Anda bisa mendapatkan perilaku yang berbeda - tapi itu didasarkan pada tipe waktu kompilasi dari ekspresi. Misalnya, stringberikan ==(string, string):

string x = new string("foo".ToCharArray());
string y = new string("foo".ToCharArray());
Console.WriteLine(x == y); // True
Console.WriteLine((object) x == (object) y); // False

Di sini perbandingan pertama menggunakan operator kelebihan beban, tetapi yang kedua menggunakan perbandingan referensi "default".

Dalam VB, =operator melakukan lebih banyak pekerjaan - bahkan tidak setara dengan menggunakan object.Equals(x, y), karena hal-hal seperti Option Comparedapat mempengaruhi bagaimana teks dibandingkan.

Pada dasarnya operator tidak bekerja dengan cara yang sama dan tidak dimaksudkan untuk bekerja dengan cara yang sama.

Jon Skeet
sumber
17
+1 Saya tahu Anda akan ada, Anda MENCINTAI pertanyaan misterius semacam ini :)
Abdusalam Ben Haj
3
@AbZy: Saya berharap dapat memberikan penjelasan yang lebih rinci tentang apa yang =dilakukan di VB, tetapi speknya tidak terlalu jelas.
Jon Skeet
hal yang menarik, tetapi mengubah objek menjadi dinamis berperilaku sama seperti VB
VladL
4
@ VladL: Ya, karena itu akan pergi dengan jenis waktu eksekusi, dan melakukan bool == boolperbandingan.
Jon Skeet
1
@Mahdi Lobo mungkin telah memberikan kode, tetapi jawabannya juga salah, tidak seperti Jon.
Servy
79

Selain jawaban Jon yang menjelaskan sisi C #, inilah yang dilakukan VB:

Dalam VB dengan Option Strict On, perbandingan melalui = selalu menguji kesetaraan nilai dan tidak pernah untuk kesetaraan referensi. Bahkan, kode Anda bahkan tidak dapat dikompilasi setelah Anda beralih Option Strict Onkarena System.Objecttidak mendefinisikan Operator=. Anda harus selalu memiliki opsi ini, itu menangkap bug lebih efektif daripada penangkap lalat venus (meskipun dalam kasus khusus Anda perilaku longgar ini benar-benar melakukan hal yang benar). 1

Bahkan, dengan Option Strict On, VB berperilaku lebih ketat daripada C #: Dalam C #, a == b baik memicu panggilan ke SomeType.operator==(a, b)atau, jika ini tidak ada, meminta referensi perbandingan kesetaraan (yang setara dengan panggilan object.ReferenceEquals(a, b)).

Di VB di sisi lain, perbandingan a = b selalu memanggil operator kesetaraan. 2 Jika Anda ingin menggunakan perbandingan kesetaraan referensi, Anda harus menggunakan a Is b(yang, sekali lagi, sama dengan Object.ReferenceEquals(a, b)).


1) Berikut ini indikasi bagus mengapa menggunakanOption Strict Off adalah ide yang buruk: Saya sudah VB.NET digunakan selama hampir satu dekade, dari sebelum rilis resmi NET sampai beberapa tahun yang lalu, dan saya sudah benar-benar tidak tahu apa yang a = bdilakukannya dengan Option Strict Off. Ia melakukan semacam perbandingan kesetaraan, tetapi apa yang sebenarnya terjadi dan mengapa, tidak tahu. Ini lebih kompleks daripada fitur C # dynamic, (karena itu bergantung pada API yang terdokumentasi dengan baik). Inilah yang dikatakan MSDN:

Karena Option Strict Onmemberikan pengetikan yang kuat , mencegah konversi tipe yang tidak diinginkan dengan kehilangan data, melarang pengikatan yang terlambat, dan meningkatkan kinerja, penggunaannya sangat disarankan.

2) Jon telah menyebutkan satu pengecualian, string, di mana perbandingan kesetaraan melakukan beberapa hal lagi untuk alasan kompatibilitas mundur.

Konrad Rudolph
sumber
4
+1. Saya pikir ini adalah satu kasus di mana para perancang VB.NET berhasil membuat bahasa "hanya berfungsi" untuk programmer yang berasal dari VB6 dan VBA, di mana OOP jauh kurang menonjol dan konsep persamaan referensi jauh kurang penting. Seorang VB coder dapat menulis kode kerja yang baik tanpa berpikir banyak tentang objek dan sebagainya.
John M Gant
5
+1 Ini tidak ter-upgrade sebanyak yang seharusnya. Tidak menggunakan Option Strict Onharus dianggap sebagai tindak pidana ...
Deer Hunter
1
@JohnMGant: Seorang pembuat kode yang tidak memahami pentingnya identitas referensi mungkin dapat menulis kode yang berfungsi, tetapi tidak mungkin untuk benar-benar mengetahui hal-hal apa yang dapat diubah dengan aman, perubahan apa yang akan selalu merusak barang, dan perubahan apa yang mungkin terjadi tampaknya berfungsi tetapi menyebabkan efek samping buruk yang tidak diinginkan (misalnya menyebabkan apa yang seharusnya menjadi referensi ke objek yang bisa berubah yang memiliki status yang sama untuk menjadi referensi ke objek yang sama). Jika objek jarang bermutasi, perubahan seperti itu mungkin tidak menyebabkan masalah langsung, tetapi dapat membuat bug yang sulit ditemukan muncul kemudian.
supercat
4

Instans objek tidak dibandingkan dengan operator "==". Anda harus menggunakan metode "sama dengan". Dengan "==" operator membandingkan referensi, bukan objek.

Coba ini:

public class MyObject
{
    public MyObject(String v)
    {
        Value = v;
    }
    public String Value { get; set; }
}

MyObject a = new MyObject("a");
MyObject b = new MyObject("a");
if(a==b){
    Debug.WriteLine("a reference is equal to b reference");
}else{
    Debug.WriteLine("a reference is not equal to b reference");
}
if (a.Equals(b)) {
    Debug.WriteLine("a object is equal to b object");
} else {
    Debug.WriteLine("a object is not equal to b object");
}

Hasil:

a reference is not equal to b reference
a object is not equal to b object

Sekarang, coba ini:

public class MyObject
{
    public MyObject(String v)
    {
        Value = v;
    }
    public String Value { get; set; }

    public bool Equals(MyObject o)
    {
        return (Value.CompareTo(o.Value)==0);
    }
}
MyObject a = new MyObject("a");
MyObject b = new MyObject("a");
if(a==b){
    Debug.WriteLine("a reference is equal to b reference");
}else{
    Debug.WriteLine("a reference is not equal to b reference");
}
if (a.Equals(b)) {
    Debug.WriteLine("a object is equal to b object");
} else {
    Debug.WriteLine("a object is not equal to b object");
}

Hasil:

a reference is not equal to b reference
a object is equal to b object
Lobo
sumber
1
Ini hanya karena Anda tidak menimpanya operator ==. Jika Anda mengesampingkan operator itu dan tidak sama maka output Anda akan terbalik. Tidak ada yang melekat untuk membandingkan referensi tentang operator ==dan tidak ada yang melekat tentang membandingkan nilai dalam Equals. Mereka hanyalah dua cara untuk menentukan kesetaraan; keduanya memiliki implementasi standar dari perbandingan referensi, dan keduanya dapat diganti untuk melakukan apa pun yang Anda inginkan. Satu-satunya perbedaan lainnya adalah itu Equalsvirtual, dan operator ==tidak.
Servy
1
@Servy: Perhatikan bahwa Anda tidak dapat mengesampingkan == - Anda hanya dapat membebaninya .
Jon Skeet
1
Maaf, -1. Jawaban ini benar-benar salah dan seharusnya tidak diterima.
Konrad Rudolph
Di suatu tempat ada pertanyaan Java menunggu jawaban ini.
Chad Schouggins
3

Masalahnya adalah bahwa operator == di C # adalah panggilan ke metode statis (well, mungkin tidak secara teknis, tetapi bisa juga seperti itu) berdasarkan jenis waktu kompilasi dari dua parameter. Apa tipe runtime aktual dari objek-objek itu tidak penting.

Berdasarkan tipe waktu kompilasi itu kompiler akan menentukan implementasi apa yang operator ==akan digunakan. Mungkin menggunakan defaultobject implementasi , mungkin menggunakan salah satu kelebihan numerik yang disediakan oleh bahasa, atau bisa juga implementasi yang ditentukan pengguna.

Ini berbeda dari VB karena VB tidak menentukan implementasi pada waktu kompilasi. Ia menunggu sampai runtime dan memeriksa dua parameter yang diberikan untuk menentukan implementasi ==operator yang harus digunakan.

Kode Anda mengandung nilai boolean, tetapi mereka berada dalam variabel yang bertipe object. Karena variabel bertipe object, kompiler C # menggunakan objectimplementasi ==, yang membandingkan referensi , bukan instance objek. Karena nilai boolean adalah kotak, mereka tidak memiliki referensi yang sama, meskipun nilainya sama.

Kode VB tidak peduli apa jenis variabelnya. Itu menunggu sampai runtime dan kemudian memeriksa dua variabel, melihat bahwa mereka sebenarnya dari kedua jenis boolean, dan menggunakan ==implementasi operator boolean . Implementasi itu membandingkan nilai-nilai boolean, bukan referensi mereka (dan boolean akan dibuka kotaknya sebelum memanggil panggilan operator itu, sehingga perbandingan referensi bahkan tidak masuk akal lagi). Karena nilai-nilai boolean sama, itu mengembalikan nilai true.

Melayani
sumber
Itu terlihat bagus untuk C #; Saya tidak cukup tahu persis apa yang harus =dikatakan dalam VB.
Jon Skeet
@ JonSkeet Cukup adil.
Servy
Per msdn.microsoft.com/en-us/library/cey92b0t(v=vs.110).aspx , di bagian "typeless Programming dengan Relational Operator Perbandingan": =, bersama dengan semua operator relasional yang lain perbandingan seperti <, >=, dll , diberikan perlakuan khusus ketika kedua atau kedua sisi operator Object. Perlakuan khusus ini dilakukan agar pemrogram VB6, yang terbiasa menggunakan tipe yang dikenal sebagai Variantpre-.NET VB, dapat memanfaatkan ObjectVB.Net dengan cara yang telah mereka gunakan Variantsebelumnya.
rskar
Dengan kata lain, dan mengesampingkan efek kelebihan beban dan Option Strict On, VB= bias menuju unboxing Objectsampai itu bisa sampai ke String atau numerik.
rskar