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.Equals
mana kurangnya NotEquals
mitra menunjukkan bahwa kerangka kerja menganggap !Equals()
objek tidak setara dan hanya itu. Selain itu, kelas suka Dictionary
dan 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 Equals
metode. Keputusan untuk mengharuskan operator == dan! = Untuk didefinisikan secara bersamaan oleh pengguna terkait murni dengan desain bahasa dan bukan objek semantik sejauh menyangkut .NET.
sumber
Jawaban:
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:
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:
sama validnya dengan yang ini:
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 #:
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:
kamu akan mendapatkan
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
sumber
==
mengembalikan apa pun selain abool
? Jika Anda mengembalikan suatuint
, Anda tidak dapat lagi menggunakannya sebagai pernyataan bersyarat misalnya.if(a == b)
tidak lagi dikompilasi. Apa gunanya?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:
Dan
a == true
kembalifalse
dana == false
juga kembalifalse
.sumber
a
bukan boolean Anda harus membandingkannya dengan enum tiga negara, bukan nyatatrue
ataufalse
.a == true
inifalse
maka saya akan selalu mengharapkana != true
untuk menjaditrue
, meskipuna == false
menjadifalse
==
, bukan mengapa mereka harus dipaksa untuk menimpa keduanya.!=
akan benar. Dalam kasus yang sangat tidak umum di mana kita inginx == y
danx != y
keduanya 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!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
return
keluar 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. Meskipunreturn
teknik yang sama dapat diterapkan, kode tersebut mungkin tidak secantik itu.sumber
!
mengunci Anda ke dalam satu definisi untuk kesetaraan, di mana seorang pengkode informasi mungkin dapat mengoptimalkan keduanya==
dan!=
.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
dan! = as
Jika Anda (atau kompiler jika melakukannya secara implisit) adalah untuk mengimplementasikan! = As
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.
sumber
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.
sumber
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.
sumber
Inilah yang terlintas dalam pikiran saya:
false
keduanya untuk==
dan!=
(yaitu jika mereka tidak dapat dibandingkan karena beberapa alasan)sumber
Dictionary
danList
jika Anda menggunakan jenis khusus Anda dengannya.Dictionary
menggunakanGetHashCode
danEquals
, bukan operator.List
menggunakanEquals
.Yah, itu mungkin hanya pilihan desain, tetapi seperti yang Anda katakan,
x!= y
tidak 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 ...
sumber
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
sumber
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 (
ref
return dan penduduk setempat, misalnya), dan banyak contoh menerapkan fitur tidak dalam CLR itu sendiri (misalnya:using
,lock
,foreach
, dll).sumber
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.
sumber
Null
hanya akan menjadi masalah karenanull
merupakan input yang valid==
, yang seharusnya tidak, meskipun jelas, itu sangat nyaman. Secara pribadi, saya hanya berpikir itu memungkinkan untuk sejumlah besar, pemrograman yang ceroboh.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.
sumber
operator ==
dan kompilator akan menyimpulkanoperator !=
. Lagipula, inilah gunanyaoperator +
danoperator +=
.a != b
sebagai!(a == b)
... (yang tidak apa C # tidak).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.
sumber