Mengapa kita harus mendefinisikan == dan! = Dalam C #?

346

Compiler C # mensyaratkan bahwa kapan pun tipe kustom mendefinisikan operator ==, ia juga harus mendefinisikan !=(lihat di sini ).

Mengapa?

Saya ingin tahu mengapa para perancang menganggap itu perlu dan mengapa kompiler tidak dapat melakukan default untuk implementasi yang masuk akal bagi salah satu operator ketika hanya yang lain yang hadir. Misalnya, Lua memungkinkan Anda menentukan hanya operator kesetaraan dan Anda mendapatkan yang lain secara gratis. C # dapat melakukan hal yang sama dengan meminta Anda untuk mendefinisikan == atau keduanya == dan! = Dan kemudian secara otomatis mengkompilasi operator yang hilang! = !(left == right).

Saya mengerti bahwa ada kasus sudut aneh di mana beberapa entitas mungkin tidak sama atau tidak sama, (seperti IEEE-754 NaN), tetapi itu tampak seperti pengecualian, bukan aturan. Jadi ini tidak menjelaskan mengapa desainer kompiler C # membuat pengecualian aturan.

Saya telah melihat kasus-kasus pengerjaan yang buruk di mana operator kesetaraan didefinisikan, maka operator ketidaksetaraan adalah copy-paste dengan masing-masing dan setiap perbandingan dibalik dan setiap && beralih ke || (Anda mendapatkan intinya ... pada dasarnya! (a == b) diperluas melalui aturan De Morgan). Itu praktik buruk yang bisa dihilangkan oleh kompiler dengan desain, seperti halnya dengan Lua.

Catatan: Hal yang sama berlaku untuk operator <> <=> =. Saya tidak bisa membayangkan kasus-kasus di mana Anda harus mendefinisikan ini dengan cara yang tidak wajar. Lua memungkinkan Anda mendefinisikan hanya <dan <= dan mendefinisikan> = dan> secara alami melalui negasi pembentuk. Mengapa C # tidak melakukan hal yang sama (setidaknya 'secara default')?

EDIT

Rupanya ada alasan yang sah untuk memungkinkan pemrogram menerapkan pemeriksaan untuk kesetaraan dan ketidaksetaraan sesuka mereka. Beberapa jawaban menunjuk pada kasus-kasus di mana itu mungkin menyenangkan.

Kernel pertanyaan saya, bagaimanapun, adalah mengapa ini secara paksa diperlukan dalam C # ketika biasanya itu tidak diperlukan secara logis ?

Ini juga sangat kontras dengan pilihan desain untuk antarmuka .NET seperti Object.Equals, di IEquatable.Equals IEqualityComparer.Equalsmana kurangnya NotEqualsmitra menunjukkan bahwa kerangka kerja menganggap !Equals()objek tidak setara dan hanya itu. Selain itu, kelas suka Dictionarydan metode suka .Contains()bergantung secara eksklusif pada antarmuka yang disebutkan di atas dan tidak menggunakan operator secara langsung bahkan jika mereka didefinisikan. Bahkan, ketika ReSharper menghasilkan anggota kesetaraan, ia mendefinisikan keduanya ==dan !=dalam hal Equals()dan bahkan kemudian hanya jika pengguna memilih untuk menghasilkan operator sama sekali. Operator kesetaraan tidak diperlukan oleh kerangka kerja untuk memahami kesetaraan objek.

Pada dasarnya, .NET framework tidak peduli dengan operator ini, ia hanya peduli pada beberapa Equalsmetode. Keputusan untuk mengharuskan operator == dan! = Untuk didefinisikan secara bersamaan oleh pengguna terkait murni dengan desain bahasa dan bukan objek semantik sejauh menyangkut .NET.

Stefan Dragnev
sumber
24
+1 untuk pertanyaan yang sangat bagus. Tampaknya tidak ada alasan sama sekali ... tentu saja kompiler dapat mengasumsikan bahwa a! = B dapat diterjemahkan ke dalam! (A == b) kecuali jika dikatakan sebaliknya. Saya ingin tahu mengapa mereka memutuskan untuk melakukan ini juga.
Patrick87
16
Ini adalah pertanyaan tercepat yang pernah saya lihat terpilih dan disukai.
Christopher Currens
26
Saya yakin @Eric Lippert dapat memberikan jawaban yang bagus juga.
Yuck
17
"Ilmuwan itu bukanlah orang yang memberikan jawaban yang benar, dia orang yang mengajukan pertanyaan yang benar". Inilah sebabnya mengapa di SE, pertanyaan didorong. Dan itu berhasil!
Adriano Carneiro
4
Pertanyaan yang sama untuk Python: stackoverflow.com/questions/4969629/…
Josh Lee

Jawaban:

161

Saya tidak bisa berbicara untuk perancang bahasa, tetapi dari apa yang saya bisa alasankan, sepertinya itu disengaja, keputusan desain yang tepat.

Melihat kode F # dasar ini, Anda dapat mengkompilasinya ke perpustakaan yang berfungsi. Ini adalah kode hukum untuk F #, dan hanya membebani operator kesetaraan, bukan kesenjangan:

module Module1

type Foo() =
    let mutable myInternalValue = 0
    member this.Prop
        with get () = myInternalValue
        and set (value) = myInternalValue <- value

    static member op_Equality (left : Foo, right : Foo) = left.Prop = right.Prop
    //static member op_Inequality (left : Foo, right : Foo) = left.Prop <> right.Prop

Ini persis seperti apa. Ini menciptakan pembanding kesetaraan ==hanya pada , dan memeriksa untuk melihat apakah nilai-nilai internal kelas sama.

Meskipun Anda tidak bisa membuat kelas seperti ini di C #, Anda bisa menggunakan yang dikompilasi untuk .NET. Jelas itu akan menggunakan operator kelebihan beban kami untuk == Jadi, untuk apa runtime digunakan !=?

Standar C # EMCA memiliki sejumlah peraturan (bagian 14.9) yang menjelaskan cara menentukan operator mana yang akan digunakan ketika mengevaluasi kesetaraan. Untuk membuatnya terlalu disederhanakan dan dengan demikian tidak sepenuhnya akurat, jika jenis yang dibandingkan adalah dari jenis yang sama dan ada operator kesetaraan yang kelebihan beban, ia akan menggunakan kelebihan itu dan bukan operator kesetaraan referensi standar yang diwarisi dari Object. Maka, tidak mengherankan, jika hanya ada satu operator, ia akan menggunakan operator kesetaraan referensi default, yang dimiliki semua objek, tidak ada kelebihan untuk itu. 1

Mengetahui bahwa inilah masalahnya, pertanyaan sebenarnya adalah: Mengapa ini dirancang dengan cara ini dan mengapa kompiler tidak mengetahuinya sendiri? Banyak orang mengatakan ini bukan keputusan desain, tapi saya suka berpikir itu dipikirkan dengan cara ini, terutama mengenai fakta bahwa semua objek memiliki operator kesetaraan standar.

Jadi, mengapa kompiler tidak secara otomatis membuat !=operator? Saya tidak tahu pasti kecuali seseorang dari Microsoft mengonfirmasi hal ini, tetapi ini adalah apa yang dapat saya tentukan berdasarkan alasan fakta-faktanya.


Untuk mencegah perilaku yang tidak terduga

Mungkin saya ingin melakukan perbandingan nilai ==untuk menguji kesetaraan. Namun, ketika sampai pada !=saya tidak peduli sama sekali jika nilainya sama kecuali jika rujukannya sama, karena agar program saya menganggapnya sama, saya hanya peduli jika rujukannya cocok. Bagaimanapun, ini sebenarnya diuraikan sebagai perilaku default C # (jika kedua operator tidak kelebihan beban, seperti dalam kasus beberapa pustaka .net yang ditulis dalam bahasa lain). Jika kompiler menambahkan kode secara otomatis, saya tidak bisa lagi mengandalkan kompiler untuk kode output yang seharusnya sesuai. Kompiler tidak boleh menulis kode tersembunyi yang mengubah perilaku Anda, terutama ketika kode yang Anda tulis berada dalam standar C # dan CLI.

Dalam hal itu memaksa Anda untuk membebani itu, alih-alih pergi ke perilaku default, saya hanya bisa dengan tegas mengatakan bahwa itu ada dalam standar (EMCA-334 17.9.2) 2 . Standar tidak menjelaskan alasannya. Saya percaya ini karena fakta bahwa C # meminjam banyak perilaku dari C ++. Lihat di bawah untuk lebih lanjut tentang ini.


Saat Anda menimpa !=dan ==, Anda tidak harus mengembalikan bool.

Ini kemungkinan alasan lain. Di C #, fungsi ini:

public static int operator ==(MyClass a, MyClass b) { return 0; }

sama validnya dengan yang ini:

public static bool operator ==(MyClass a, MyClass b) { return true; }

Jika Anda mengembalikan sesuatu selain bool, kompiler tidak dapat secara otomatis menyimpulkan tipe yang berlawanan. Selain itu, dalam kasus di mana operator Anda mengembalikan bool, tidak masuk akal bagi mereka untuk membuat kode menghasilkan yang hanya akan ada dalam satu kasus tertentu atau, seperti yang saya katakan di atas, kode yang menyembunyikan perilaku default CLR.


C # meminjam banyak dari C ++ 3

Ketika C # diperkenalkan, ada sebuah artikel di majalah MSDN yang menulis, berbicara tentang C #:

Banyak pengembang berharap ada bahasa yang mudah untuk ditulis, dibaca, dan dipelihara seperti Visual Basic, tetapi itu masih memberikan kekuatan dan fleksibilitas C ++.

Ya tujuan desain untuk C # adalah untuk memberikan jumlah daya yang hampir sama dengan C ++, hanya mengorbankan sedikit demi kenyamanan seperti keamanan jenis yang kaku dan pengumpulan sampah. C # sangat dimodelkan setelah C ++.

Anda mungkin tidak terkejut mengetahui bahwa dalam C ++, operator kesetaraan tidak harus mengembalikan bool , seperti yang ditunjukkan dalam contoh program ini

Sekarang, C ++ tidak secara langsung mengharuskan Anda untuk membebani operator pelengkap. Jika Anda mengkompilasi kode dalam program contoh, Anda akan melihatnya berjalan tanpa kesalahan. Namun, jika Anda mencoba menambahkan baris:

cout << (a != b);

kamu akan mendapatkan

kesalahan kompiler C2678 (MSVC): binary '! =': tidak ada operator yang mengambil operan kiri dari tipe 'Tes' (atau tidak ada konversi yang dapat diterima) `.

Jadi, sementara C ++ sendiri tidak mengharuskan Anda untuk membebani secara berpasangan, itu tidak akan membiarkan Anda menggunakan operator kesetaraan yang Anda belum kelebihan pada kelas kustom. Ini valid dalam .NET, karena semua objek memiliki objek default; C ++ tidak.


1. Sebagai catatan tambahan, standar C # masih mengharuskan Anda untuk membebani pasangan operator jika Anda ingin membebani salah satu dari keduanya. Ini adalah bagian dari standar dan bukan hanya kompiler . Namun, aturan yang sama tentang penentuan operator mana yang akan dihubungi berlaku ketika Anda mengakses perpustakaan .net yang ditulis dalam bahasa lain yang tidak memiliki persyaratan yang sama.

2. EMCA-334 (pdf) ( http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-334.pdf )

3. Dan Java, tapi itu bukan intinya di sini

Christopher Currens
sumber
75
" Anda tidak harus mengembalikan bool " .. Saya pikir itu jawaban kami di sana; sudah selesai dilakukan dengan baik. Jika! (O1 == o2) bahkan tidak terdefinisi dengan baik, maka Anda tidak dapat mengharapkan standar untuk mengandalkannya.
Brian Gordon
10
Ironisnya dalam sebuah topik tentang operator logis Anda telah menggunakan double-negative dalam kalimat, "C # tidak memungkinkan Anda untuk tidak hanya menimpa salah satu dari keduanya" yang sebenarnya secara logis berarti "C # memungkinkan Anda untuk menimpa hanya salah satu dari keduanya".
Dan Diplo
13
@Dan Diplo, Tidak apa-apa, Ini tidak seperti saya tidak dilarang untuk tidak melakukan itu atau tidak.
Christopher Currens
6
Jika benar, saya menduga # 2 benar; tetapi sekarang pertanyaannya menjadi, mengapa dibiarkan ==mengembalikan apa pun selain a bool? Jika Anda mengembalikan suatu int, Anda tidak dapat lagi menggunakannya sebagai pernyataan bersyarat misalnya. if(a == b)tidak lagi dikompilasi. Apa gunanya?
BlueRaja - Danny Pflughoeft
2
Anda dapat mengembalikan objek yang memiliki konversi implisit ke bool (operator true / false) tetapi saya masih gagal melihat di mana itu akan berguna.
Stefan Dragnev
53

Mungkin karena jika seseorang perlu menerapkan logika tiga nilai (yaitu null). Dalam kasus seperti itu - SQL standar ANSI, misalnya - operator tidak bisa begitu saja dinegasikan tergantung pada input.

Anda dapat memiliki kasus di mana:

var a = SomeObject();

Dan a == truekembali falsedan a == falsejuga kembali false.

Yuck
sumber
1
Saya akan mengatakan dalam kasus itu, karena abukan boolean Anda harus membandingkannya dengan enum tiga negara, bukan nyata trueatau false.
Brian Gordon
18
Ngomong-ngomong, aku tidak percaya tidak ada yang mereferensikan lelucon FileNotFound ..
Brian Gordon
28
Jika a == trueini falsemaka saya akan selalu mengharapkan a != trueuntuk menjadi true, meskipun a == falsemenjadifalse
Fede
20
@Fede memukul paku di kepala. Ini benar-benar tidak ada hubungannya dengan pertanyaan - Anda menjelaskan mengapa seseorang mungkin ingin menimpa ==, bukan mengapa mereka harus dipaksa untuk menimpa keduanya.
BlueRaja - Danny Pflughoeft
6
@Yuck - Ya, ada implementasi standar yang baik: ini adalah kasus umum. Bahkan dalam contoh Anda, implementasi default !=akan benar. Dalam kasus yang sangat tidak umum di mana kita ingin x == ydan x != ykeduanya salah (saya tidak bisa memikirkan satu contoh yang masuk akal) , mereka masih bisa mengesampingkan implementasi default dan menyediakan sendiri. Selalu jadikan kasus umum ini sebagai default!
BlueRaja - Danny Pflughoeft
25

Selain itu C # tidak mendukung C ++ di banyak bidang, penjelasan terbaik yang dapat saya pikirkan adalah bahwa dalam beberapa kasus Anda mungkin ingin mengambil pendekatan yang sedikit berbeda untuk membuktikan "bukan kesetaraan" daripada membuktikan "kesetaraan".

Tentunya dengan perbandingan string, misalnya, Anda bisa saja menguji kesetaraan dan returnkeluar dari loop ketika Anda melihat karakter yang tidak cocok. Namun, mungkin tidak begitu bersih dengan masalah yang lebih rumit. The mekar Filter datang ke pikiran; sangat mudah untuk dengan cepat mengetahui apakah elemen tersebut tidak ada di set, tetapi sulit untuk mengetahui apakah elemen tersebut ada di set. Meskipun returnteknik yang sama dapat diterapkan, kode tersebut mungkin tidak secantik itu.

Brian Gordon
sumber
6
Contoh yang bagus; Saya pikir Anda ke alasan mengapa. Sederhana !mengunci Anda ke dalam satu definisi untuk kesetaraan, di mana seorang pengkode informasi mungkin dapat mengoptimalkan keduanya == dan !=.
Yuck
13
Jadi ini menjelaskan mengapa mereka harus diberi pilihan untuk menimpa keduanya, bukan mengapa mereka harus dipaksa .
BlueRaja - Danny Pflughoeft
1
Dalam nada itu, mereka tidak harus mengoptimalkan keduanya, mereka hanya harus memberikan definisi yang jelas tentang keduanya.
Jesper
22

Jika Anda melihat implementasi kelebihan == dan! = Di sumber .net, mereka sering tidak mengimplementasikan! = As! (Kiri == kanan). Mereka menerapkannya sepenuhnya (seperti ==) dengan logika negated. Misalnya, DateTime mengimplementasikan == as

return d1.InternalTicks == d2.InternalTicks;

dan! = as

return d1.InternalTicks != d2.InternalTicks;

Jika Anda (atau kompiler jika melakukannya secara implisit) adalah untuk mengimplementasikan! = As

return !(d1==d2);

maka Anda membuat asumsi tentang implementasi internal == dan! = dalam hal-hal yang dirujuk oleh kelas Anda. Menghindari anggapan itu mungkin merupakan filosofi di balik keputusan mereka.

kapak - dilakukan dengan SOverflow
sumber
17

Untuk menjawab hasil edit Anda, mengenai mengapa Anda dipaksa untuk menimpa keduanya jika Anda menimpa satu, itu semua ada dalam warisan.

Jika Anda menimpa ==, kemungkinan besar akan memberikan semacam persamaan semantik atau struktural (misalnya, DateTimes sama jika properti InternalTicksnya sama walaupun melalui mereka mungkin contoh berbeda), maka Anda mengubah perilaku default operator dari Objek, yang merupakan induk dari semua objek .NET. Operator == adalah, dalam C #, metode, yang basis implementasi Object.operator (==) melakukan perbandingan referensial. Object.operator (! =) Adalah metode lain yang berbeda, yang juga melakukan perbandingan referensial.

Dalam hampir semua kasus lain dari metode overriding, akan tidak masuk akal untuk menganggap bahwa menimpa satu metode juga akan menghasilkan perubahan perilaku menjadi metode antonim. Jika Anda membuat kelas dengan metode Increment () dan Decrement (), dan mengesampingkan Increment () di kelas anak, apakah Anda berharap Decrement () juga akan ditimpa dengan kebalikan dari perilaku Anda yang ditimpa? Kompiler tidak dapat dibuat cukup pintar untuk menghasilkan fungsi terbalik untuk setiap implementasi operator dalam semua kasus yang mungkin.

Namun, operator, meskipun diimplementasikan sangat mirip dengan metode, secara konseptual bekerja berpasangan; == dan! =, <dan>, dan <= dan> =. Dalam hal ini akan tidak masuk akal dari sudut pandang konsumen untuk berpikir bahwa! = Bekerja dengan cara yang berbeda dari ==. Jadi, kompilator tidak dapat dibuat untuk berasumsi bahwa a! = B ==! (A == b) dalam semua kasus, tetapi umumnya diharapkan bahwa == dan! = Harus beroperasi dengan cara yang sama, sehingga kompilator memaksa Anda menerapkan berpasangan, namun Anda akhirnya melakukan itu. Jika, untuk kelas Anda, a! = B ==! (A == b), maka cukup terapkan operator! = Menggunakan! (==), tetapi jika aturan itu tidak berlaku dalam semua kasus untuk objek Anda (misalnya , jika perbandingan dengan nilai tertentu, sama atau tidak sama, tidak valid), maka Anda harus lebih pintar daripada IDE.

Pertanyaan NYATA yang harus ditanyakan adalah mengapa <dan> dan <= dan> = berpasangan untuk operator komparatif yang harus diimplementasikan secara bersamaan, ketika dalam istilah numerik! (A <b) == a> = b dan! (A> b) == a <= b. Anda harus diminta untuk mengimplementasikan keempatnya jika menimpa satu, dan Anda mungkin juga harus ditimpa == (dan! =) Juga, karena (a <= b) == (a == b) jika a secara semantik sama dengan b.

KeithS
sumber
14

Jika Anda overload == untuk jenis kustom Anda, dan tidak! = Maka itu akan ditangani oleh operator! = Objek! = Objek karena semuanya berasal dari objek, dan ini akan jauh berbeda dari CustomType! = CustomType.

Juga pembuat bahasa mungkin menginginkannya dengan cara ini untuk memberikan fleksibilitas yang paling besar untuk coders, dan juga agar mereka tidak membuat asumsi tentang apa yang ingin Anda lakukan.

Chris Mullins
sumber
Anda menginspirasi pertanyaan ini: stackoverflow.com/questions/6919259/…
Brian Gordon
2
Alasan fleksibilitas juga dapat diterapkan pada banyak fitur hebat yang mereka putuskan untuk tinggalkan, misalnya blogs.msdn.com/b/ericlippert/archive/2011/06/23/… .Jadi itu tidak menjelaskan pilihan mereka untuk implementasi operator kesetaraan.
Stefan Dragnev
Itu sangat menarik. Sepertinya itu akan menjadi fitur yang berguna. Saya tidak tahu mengapa mereka meninggalkan itu.
Chris Mullins
11

Inilah yang terlintas dalam pikiran saya:

  • Bagaimana jika menguji ketimpangan jauh lebih cepat daripada menguji kesetaraan?
  • Bagaimana jika dalam beberapa kasus Anda ingin mengembalikan falsekeduanya untuk ==dan!= (yaitu jika mereka tidak dapat dibandingkan karena beberapa alasan)
Dan Abramov
sumber
5
Dalam kasus pertama, Anda dapat menerapkan pengujian kesetaraan dalam hal pengujian ketidaksetaraan.
Stefan Dragnev
Kasing kedua akan memecah struktur seperti Dictionarydan Listjika Anda menggunakan jenis khusus Anda dengannya.
Stefan Dragnev
2
@Stefan: Dictionarymenggunakan GetHashCodedan Equals, bukan operator. Listmenggunakan Equals.
Dan Abramov
6

Yah, itu mungkin hanya pilihan desain, tetapi seperti yang Anda katakan, x!= ytidak harus sama dengan !(x == y). Dengan tidak menambahkan implementasi default, Anda yakin bahwa Anda tidak bisa lupa untuk mengimplementasikan implementasi tertentu. Dan jika memang sepele seperti yang Anda katakan, Anda bisa menerapkan satu menggunakan yang lain. Saya tidak melihat bagaimana ini adalah 'praktik yang buruk'.

Mungkin ada beberapa perbedaan lain antara C # dan Lua juga ...

GolezTrol
sumber
1
Merupakan praktik yang sangat baik untuk menerapkan satu dalam hal yang lain. Saya baru saja melihatnya dilakukan sebaliknya - yang rawan kesalahan.
Stefan Dragnev
3
Itu adalah masalah programmer, bukan masalah C #. Programmer yang gagal menerapkan hak ini, akan gagal di area lain juga.
GolezTrol
@ GolezTrol: Bisakah Anda menjelaskan referensi Lua itu? Saya sama sekali tidak akrab dengan Lua, tetapi referensi Anda tampaknya mengisyaratkan sesuatu.
zw324
@Ziyao Wei: OP menunjukkan bahwa Lua melakukan ini secara berbeda dari C # (Lua tampaknya memang menambah rekan standar untuk operator yang disebutkan). Hanya mencoba meremehkan perbandingan seperti itu. Jika tidak ada perbedaan sama sekali maka setidaknya salah satu dari mereka tidak memiliki hak untuk hidup. Harus mengatakan aku juga tidak kenal Lua.
GolezTrol
6

Kata-kata kunci dalam pertanyaan Anda adalah " mengapa " dan " harus ".

Hasil dari:

Menjawab dengan cara ini karena mereka mendesainnya demikian, memang benar ... tetapi tidak menjawab bagian "mengapa" dari pertanyaan Anda.

Menjawab bahwa kadang-kadang mungkin membantu untuk menimpa kedua hal ini secara independen, itu benar ... tetapi tidak menjawab bagian "harus" dari pertanyaan Anda.

Saya pikir jawaban sederhana adalah bahwa tidak ada alasan yang meyakinkan mengapa C # mengharuskan Anda untuk menimpa keduanya.

Bahasa harus memungkinkan Anda untuk menimpa hanya ==, dan memberikan implementasi standar !=yang !itu. Jika Anda ingin menimpa !=juga, miliki.

Itu bukan keputusan yang baik. Bahasa desain manusia, manusia tidak sempurna, C # tidak sempurna. Mengangkat bahu dan QED

Greg Hendershott
sumber
5

Hanya dengan menambahkan jawaban yang sangat baik di sini:

Pertimbangkan apa yang akan terjadi di debugger , ketika Anda mencoba masuk ke !=operator dan berakhir di ==operator sebagai gantinya! Bicara tentang membingungkan!

Masuk akal bahwa CLR akan memungkinkan Anda kebebasan untuk mengabaikan satu atau lebih operator - karena harus bekerja dengan banyak bahasa. Tapi ada banyak contoh C # tidak mengekspos fitur CLR ( refreturn dan penduduk setempat, misalnya), dan banyak contoh menerapkan fitur tidak dalam CLR itu sendiri (misalnya: using, lock, foreach, dll).

Andrew Russell
sumber
3

Bahasa pemrograman adalah penyusunan ulang sintaksis dari pernyataan logis yang sangat kompleks. Dengan mengingat hal itu, dapatkah Anda mendefinisikan kasus kesetaraan tanpa mendefinisikan kasus ketidaksetaraan? Jawabannya adalah tidak. Agar objek a sama dengan objek b, maka kebalikan dari objek a tidak sama dengan b juga harus benar. Cara lain untuk menunjukkan ini adalah

if a == b then !(a != b)

ini memberikan kemampuan yang pasti bagi bahasa untuk menentukan kesetaraan objek. Misalnya, perbandingan NULL! = NULL dapat melempar kunci pas ke definisi sistem kesetaraan yang tidak menerapkan pernyataan non-kesetaraan.

Sekarang, sehubungan dengan gagasan! = Hanya menjadi definisi yang dapat diganti seperti pada

if !(a==b) then a!=b

Saya tidak bisa berdebat dengan itu. Namun, kemungkinan besar keputusan oleh kelompok spesifikasi bahasa C # bahwa programmer dipaksa untuk secara eksplisit mendefinisikan kesetaraan dan dan non-kesetaraan suatu objek bersama-sama.

Josh
sumber
Nullhanya akan menjadi masalah karena nullmerupakan input yang valid ==, yang seharusnya tidak, meskipun jelas, itu sangat nyaman. Secara pribadi, saya hanya berpikir itu memungkinkan untuk sejumlah besar, pemrograman yang ceroboh.
nicodemus13
2

Singkatnya, konsistensi yang dipaksakan.

'==' dan '! =' selalu bertolak belakang, tidak peduli bagaimana Anda mendefinisikannya, didefinisikan seperti itu dengan definisi verbal mereka "sama dengan" dan "tidak sama dengan". Dengan hanya mendefinisikan salah satunya, Anda membuka diri terhadap inkonsistensi operator kesetaraan di mana '==' dan '! =' Keduanya bisa benar atau keduanya salah untuk dua nilai yang diberikan. Anda harus mendefinisikan keduanya karena ketika Anda memilih untuk mendefinisikan satu, Anda juga harus mendefinisikan yang lain dengan tepat sehingga jelas apa definisi "kesetaraan" Anda. Solusi lain untuk kompiler adalah dengan hanya membiarkan Anda menimpa '==' OR '! =' Dan membiarkan yang lain secara inheren meniadakan yang lain. Jelas, bukan itu yang terjadi dengan kompiler C # dan saya yakin ada '

Pertanyaan yang harus Anda tanyakan adalah "mengapa saya perlu mengganti operator?" Itu adalah keputusan kuat untuk dibuat yang membutuhkan alasan kuat. Untuk objek, '==' dan '! =' Bandingkan dengan referensi. Jika Anda ingin menimpa mereka untuk TIDAK membandingkan dengan referensi, Anda membuat inkonsistensi operator umum yang tidak terlihat oleh pengembang lain yang akan membaca dengan teliti kode itu. Jika Anda mencoba untuk mengajukan pertanyaan "apakah keadaan kedua instance ini setara ?," maka Anda harus mengimplementasikan IEquatible, mendefinisikan Equals () dan memanfaatkan pemanggilan metode itu.

Terakhir, IEquatable () tidak mendefinisikan NotEquals () untuk alasan yang sama: potensi untuk membuka inkonsistensi operator kesetaraan. NotEquals () harus SELALU kembali! Equals (). Dengan membuka definisi NotEquals () ke kelas yang menerapkan Equals (), Anda sekali lagi memaksa masalah konsistensi dalam menentukan kesetaraan.

Sunting: Ini hanyalah alasan saya.

Emoire
sumber
Ini tidak masuk akal. Jika alasannya adalah konsistensi maka yang sebaliknya akan berlaku: Anda hanya akan dapat mengimplementasikan operator ==dan kompilator akan menyimpulkan operator !=. Lagipula, inilah gunanya operator +dan operator +=.
Konrad Rudolph
1
foo + = bar; adalah tugas majemuk, singkatan untuk foo = foo + bar ;. Itu bukan operator.
blenderfreaky
Jika memang mereka adalah 'selalu benar berlawanan', maka itu akan menjadi alasan untuk secara otomatis menentukan a != bsebagai !(a == b)... (yang tidak apa C # tidak).
andrewf
-3

Mungkin hanya sesuatu yang mereka pikir tidak punya waktu untuk dilakukan.

Saya selalu menggunakan metode Anda ketika saya overload ==. Maka saya hanya menggunakannya di yang lain.

Anda benar, dengan sedikit pekerjaan, kompiler dapat memberikan ini kepada kami secara gratis.

Rhyous
sumber