Menurut dokumentasi ==
operator di MSDN ,
Untuk tipe nilai yang telah ditentukan, operator persamaan (==) mengembalikan true jika nilai operandnya sama, salah jika tidak. Untuk tipe referensi selain string, == mengembalikan true jika kedua operandnya merujuk ke objek yang sama. Untuk tipe string, == membandingkan nilai string. Jenis nilai yang ditentukan pengguna dapat membebani operator == (lihat operator). Demikian juga tipe referensi yang ditentukan pengguna, meskipun secara default == berperilaku seperti yang dijelaskan di atas untuk tipe referensi yang ditentukan sebelumnya dan yang ditentukan pengguna.
Jadi mengapa cuplikan kode ini gagal dikompilasi?
bool Compare<T>(T x, T y) { return x == y; }
Saya mendapatkan kesalahan Operator '==' tidak dapat diterapkan ke operan tipe 'T' dan 'T' . Saya bertanya-tanya mengapa, karena sejauh yang saya mengerti ==
operator sudah ditentukan untuk semua jenis?
Sunting: Terima kasih, semuanya. Awalnya saya tidak memperhatikan bahwa pernyataan itu hanya tentang tipe referensi. Saya juga berpikir bahwa perbandingan sedikit demi sedikit disediakan untuk semua tipe nilai, yang sekarang saya tahu tidak benar.
Tetapi, jika saya menggunakan tipe referensi, apakah ==
operator akan menggunakan perbandingan referensi yang sudah ditentukan sebelumnya, atau akankah menggunakan versi overload dari operator jika suatu tipe didefinisikan?
Sunting 2: Melalui percobaan dan kesalahan, kami mengetahui bahwa ==
operator akan menggunakan perbandingan referensi yang telah ditentukan saat menggunakan jenis generik yang tidak dibatasi. Sebenarnya, kompiler akan menggunakan metode terbaik yang dapat ditemukan untuk argumen tipe terbatas, tetapi tidak akan mencari lagi. Misalnya, kode di bawah ini akan selalu dicetak true
, bahkan ketika Test.test<B>(new B(), new B())
dipanggil:
class A { public static bool operator==(A x, A y) { return true; } }
class B : A { public static bool operator==(B x, B y) { return false; } }
class Test { void test<T>(T a, T b) where T : A { Console.WriteLine(a == b); } }
sumber
==
tidak diizinkan antara dua operan dari jenis yang sama. Ini berlaku untukstruct
tipe (kecuali tipe "yang ditentukan") yang tidak membebanioperator ==
. Sebagai contoh sederhana, coba ini:var map = typeof(string).GetInterfaceMap(typeof(ICloneable)); Console.WriteLine(map == map); /* compile-time error */
var kvp1 = new KeyValuePair<int, int>(); var kvp2 = kvp1;
, maka Anda tidak dapat memeriksakvp1 == kvp2
karenaKeyValuePair<,>
merupakan sebuah struct, itu bukan tipe yang ditentukan # C, dan itu tidak membebanioperator ==
. Namun contoh diberikanvar li = new List<int>(); var e1 = li.GetEnumerator(); var e2 = e1;
dengan mana Anda tidak dapat melakukane1 == e2
(di sini kami memiliki struct bersarangList<>.Enumerator
(disebut"List`1+Enumerator[T]"
dengan runtime) yang tidak kelebihan beban==
).bool
darivoid
...Jawaban:
"... secara default == berperilaku seperti dijelaskan di atas untuk tipe referensi yang ditentukan sebelumnya dan yang ditentukan pengguna."
Tipe T belum tentu merupakan tipe referensi, jadi kompiler tidak dapat membuat asumsi itu.
Namun, ini akan dikompilasi karena lebih eksplisit:
Tindak lanjuti pertanyaan tambahan, "Tapi, kalau-kalau saya menggunakan tipe referensi, apakah operator == akan menggunakan perbandingan referensi yang telah ditentukan, atau akankah ia menggunakan versi overload dari operator jika suatu tipe mendefinisikannya?"
Saya akan berpikir bahwa == pada Generik akan menggunakan versi overload, tetapi tes berikut menunjukkan sebaliknya. Menarik ... Saya ingin tahu kenapa! Jika ada yang tahu, silakan berbagi.
Keluaran
Inline: Kelebihan == dipanggil
Umum:
Tekan tombol apa saja untuk melanjutkan . . .
Tindak Lanjut 2
Saya ingin menunjukkan bahwa mengubah metode perbandingan ke
menyebabkan operator == kelebihan beban dipanggil. Saya kira tanpa menentukan jenisnya (sebagai mana ), kompiler tidak dapat menyimpulkan bahwa ia harus menggunakan operator yang kelebihan beban ... meskipun saya pikir itu akan memiliki informasi yang cukup untuk membuat keputusan itu bahkan tanpa menentukan jenisnya.
sumber
Equals
metode yang diganti (bukan di==
operator).==
antara tipe generikT
danT
, kelebihan beban terbaik ditemukan, mengingat kendala apa yang dipikulT
(ada aturan khusus yang tidak akan pernah mengotakkan tipe nilai untuk ini (yang akan memberikan hasil yang tidak berarti), maka harus ada beberapa kendala menjamin itu adalah tipe referensi). Dalam Follow Up 2 Anda , jika Anda datang denganDerivedTest
objek, danDerivedTest
berasal dariTest
tetapi memperkenalkan kelebihan baru==
, Anda akan memiliki "masalah" lagi. Yang kelebihan beban disebut, "dibakar" ke IL pada saat kompilasi.Seperti yang dikatakan orang lain, ini hanya akan berfungsi ketika T dibatasi menjadi tipe referensi. Tanpa kendala apa pun, Anda dapat membandingkan dengan nol, tetapi hanya nol - dan perbandingan itu akan selalu salah untuk tipe nilai yang tidak dapat dibatalkan.
Alih-alih memanggil Equals, lebih baik menggunakan
IComparer<T>
- dan jika Anda tidak memiliki informasi lagi,EqualityComparer<T>.Default
adalah pilihan yang baik:Selain dari hal lain, ini menghindari tinju / casting.
sumber
Secara umum,
EqualityComparer<T>.Default.Equals
harus melakukan pekerjaan dengan apa pun yang mengimplementasikanIEquatable<T>
, atau yang memilikiEquals
implementasi yang masuk akal .Namun, jika
==
danEquals
diimplementasikan secara berbeda karena alasan tertentu, maka pekerjaan saya pada operator generik akan berguna; mendukung versi operator (antara lain):sumber
Begitu banyak jawaban, dan tidak satu pun yang menjelaskan MENGAPA? (yang diminta secara eksplisit oleh Giovanni) ...
.NET generics tidak bertindak seperti template C ++. Di template C ++, resolusi kelebihan terjadi setelah parameter template aktual diketahui.
Dalam .NET generics (termasuk C #), resolusi kelebihan terjadi tanpa mengetahui parameter generik yang sebenarnya. Satu-satunya informasi yang dapat digunakan kompilator untuk memilih fungsi untuk memanggil berasal dari batasan tipe pada parameter generik.
sumber
==
bekerja untuk semua jenis baik itu jenis referensi atau tipe nilai. Itu seharusnya menjadi pertanyaan yang saya pikir Anda tidak menjawab.==
tidak berfungsi untuk semua tipe nilai. Lebih penting lagi, itu tidak memiliki arti yang sama untuk semua jenis, sehingga kompiler tidak tahu apa yang harus dilakukan dengannya.==
. Bisakah Anda memasukkan bagian itu juga dalam jawaban Anda karena saya kira itulah poin utama di siniKompilasi tidak dapat mengetahui T tidak dapat berupa struct (tipe nilai). Jadi Anda harus mengatakan itu hanya bisa dari jenis referensi, saya pikir:
Itu karena jika T bisa menjadi tipe nilai, mungkin ada kasus di mana
x == y
akan terbentuk buruk - dalam kasus ketika tipe tidak memiliki operator == didefinisikan. Hal yang sama akan terjadi untuk ini yang lebih jelas:Itu gagal juga, karena Anda bisa melewati tipe T yang tidak akan memiliki fungsi foo. C # memaksa Anda untuk memastikan semua tipe yang mungkin selalu memiliki fungsi foo. Itu dilakukan oleh klausa mana.
sumber
Tampaknya tanpa batasan kelas:
Orang harus menyadari bahwa sementara
class
dibatasiEquals
dalam==
operator mewarisi dariObject.Equals
, sedangkan yang menimpa structValueType.Equals
.Perhatikan bahwa:
juga memberikan kesalahan kompiler yang sama.
Sampai sekarang saya tidak mengerti mengapa memiliki perbandingan operator kesetaraan tipe nilai ditolak oleh kompiler. Saya tahu pasti, bahwa ini bekerja:
sumber
Object.Equals
melainkan menguji kesetaraan referensi. Misalnya,Compare("0", 0.ToString())
akan menghasilkan false, karena argumen akan menjadi referensi untuk string yang berbeda, keduanya memiliki nol sebagai satu-satunya karakter mereka.NullReferenceException
bisa terjadi.Baik dalam kasus saya, saya ingin menguji unit operator kesetaraan. Saya perlu memanggil kode di bawah operator kesetaraan tanpa secara eksplisit menetapkan jenis generik. Saran untuk
EqualityComparer
tidak membantuEqualityComparer
disebutEquals
metode tetapi bukan operator kesetaraan.Berikut adalah cara kerja ini dengan tipe generik dengan membangun a
LINQ
. Itu memanggil kode yang tepat untuk==
dan!=
operator:sumber
Ada entri MSDN Connect untuk ini di sini
Balasan Alex Turner dimulai dengan:
sumber
Jika Anda ingin memastikan operator jenis kustom Anda dipanggil, Anda dapat melakukannya melalui refleksi. Dapatkan saja tipe menggunakan parameter generik Anda dan ambil MethodInfo untuk operator yang diinginkan (mis. Op_Equality, op_Inequality, op_LessThan ...).
Kemudian jalankan operator menggunakan metode Invoke MethodInfo's dan meneruskan objek sebagai parameter.
Ini akan memanggil operator kelebihan beban Anda dan bukan yang ditentukan oleh kendala yang diterapkan pada parameter generik. Mungkin tidak praktis, tetapi bisa berguna untuk unit menguji operator Anda saat menggunakan kelas dasar generik yang berisi beberapa tes.
sumber
Saya menulis fungsi berikut melihat msdn terbaru. Itu dapat dengan mudah membandingkan dua objek
x
dany
:sumber
return ((IComparable)(x)).CompareTo(y) <= 0;
Hal di atas akan berfungsi karena == dirawat jika ada jenis referensi yang ditentukan pengguna.
Dalam hal tipe nilai, == dapat diganti. Dalam hal ini, "! =" Juga harus didefinisikan.
Saya pikir itu bisa menjadi alasannya, ia tidak mengizinkan perbandingan umum menggunakan "==".
sumber
==
token digunakan untuk dua operator yang berbeda. Jika untuk jenis operan tertentu terdapat kelebihan yang kompatibel dari operator persamaan, kelebihan itu akan digunakan. Kalau tidak, jika kedua operan adalah tipe referensi yang kompatibel satu sama lain, perbandingan referensi akan digunakan. Perhatikan bahwa dalamCompare
metode di atas kompiler tidak dapat mengatakan bahwa makna pertama berlaku, tetapi dapat memberitahu arti kedua berlaku, sehingga==
token akan menggunakan yang terakhir bahkan jikaT
membebani operator pemeriksa kesetaraan (misalnya jika itu jenisString
) .The
.Equals()
bekerja untuk saya sementaraTKey
adalah jenis generik.sumber
x.Id.Equals
, tidakid.Equals
. Agaknya, kompiler tahu sesuatu tentang tipex
.