Pertanyaan Haruskah IEquatable , IComparable diimplementasikan pada kelas yang tidak disegel?


Ada yang punya pendapat tentang apakah atau tidak IEquatable<T> atau IComparable<T> umumnya harus membutuhkan itu T aku s sealed (jika itu a class)?

Pertanyaan ini muncul di benak saya karena saya sedang menulis satu set kelas dasar yang dimaksudkan untuk membantu dalam implementasi kelas yang tidak dapat diubah. Bagian dari fungsionalitas yang dimaksudkan kelas dasar untuk menyediakan adalah implementasi otomatis dari perbandingan kesetaraan (menggunakan bidang kelas bersama dengan atribut yang dapat diterapkan pada bidang untuk mengontrol perbandingan kesetaraan). Ini harus cukup bagus ketika saya selesai - Saya menggunakan pohon ekspresi untuk secara dinamis membuat fungsi perbandingan terkompilasi untuk masing-masing T, sehingga fungsi perbandingan harus sangat dekat dengan kinerja fungsi perbandingan persamaan biasa. (Saya menggunakan kamus yang tidak bisa diubah System.Type dan periksa penguncian untuk menyimpan fungsi perbandingan yang dihasilkan dengan cara yang cukup berkinerja)

Satu hal yang telah terpangkas adalah fungsi apa yang digunakan untuk memeriksa kesetaraan bidang anggota. Niat awal saya adalah untuk memeriksa apakah setiap jenis bidang anggota (yang akan saya panggil X) mengimplementasikan IEquatable<X>. Namun, setelah beberapa pemikiran, saya tidak berpikir ini aman untuk digunakan kecuali X aku s sealed. Alasannya adalah bahwa jika X tidak sealed, Saya tidak tahu pasti jika X dengan tepat mendelegasikan pemeriksaan kesetaraan ke metode virtual X, sehingga memungkinkan subtipe untuk mengganti perbandingan kesetaraan.

Ini kemudian memunculkan pertanyaan yang lebih umum - jika suatu jenis tidak disegel, haruskah itu benar-benar mengimplementasikan antarmuka ini sama sekali ?? Saya akan berpikir tidak, karena saya berpendapat bahwa kontrak antarmuka adalah untuk membandingkan antara dua X jenis, bukan dua jenis yang mungkin atau tidak mungkin X (meskipun tentu saja mereka harus X atau subtipe).

apa yang kalian pikirkan? Harus IEquatable<T> dan IComparable<T> dihindari untuk kelas yang tidak ditutup? (Juga membuat saya bertanya-tanya apakah ada aturan fxcop untuk ini)

Pemikiran saya saat ini adalah agar fungsi perbandingan yang saya hasilkan hanya digunakan IEquatable<T> di kolom anggota yang T aku s sealed, dan bukannya menggunakan virtual Object.Equals(Object obj) jika T Tersembunyi bahkan jika T mengimplementasikan IEquatable<T>, karena bidang tersebut berpotensi menyimpan subtipe T dan saya meragukan sebagian besar implementasi IEquatable<T> dirancang secara tepat untuk pewarisan.


32
2017-12-08 16:56


asal


Jawaban:


Saya telah memikirkan pertanyaan ini untuk sedikit dan setelah sedikit pertimbangan, saya setuju bahwa penerapannya IEquatable<T> dan IComparable<T> hanya boleh dilakukan pada jenis yang disegel.

Aku bolak-balik sebentar tapi kemudian aku memikirkan tes berikut. Dalam situasi seperti apa yang harus kembali berikut ini salah? IMHO, 2 objek sama atau tidak.

public void EqualitySanityCheck<T>(T left, T right) where T : IEquatable<T> {
  var equals1 = left.Equals(right);
  var equals2 = ((IEqutable<T>)left).Equals(right);
  Assert.AreEqual(equals1,equals2);
}

Hasil dari IEquatable<T> pada objek yang diberikan harus memiliki perilaku yang sama dengan Object.Equals dengan asumsi komparator adalah tipe yang setara. Menerapkan IEquatable<T> dua kali dalam hierarki objek memungkinkan, dan menyiratkan, ada 2 cara berbeda untuk mengekspresikan kesetaraan dalam sistem Anda. Sangat mudah untuk membuat sejumlah skenario di mana saja IEquatable<T> dan Object.Equalsakan berbeda karena ada banyak IEquatable<T> implementasi tetapi hanya satu Object.Equals. Oleh karena itu di atas akan gagal dan membuat sedikit kebingungan dalam kode Anda.

Beberapa orang mungkin berpendapat bahwa penerapan IEquatable<T> pada titik yang lebih tinggi dalam hierarki objek adalah valid karena Anda ingin membandingkan bagian dari properti objek. Dalam hal ini Anda harus mendukung suatu IEqualityComparer<T> yang secara khusus dirancang untuk membandingkan properti tersebut.


14
2017-12-08 17:21



Saya biasanya merekomendasikan untuk tidak mengimplementasikan IEquatable <T> pada kelas yang tidak disegel, atau menerapkan IComparable non-generik pada sebagian besar, tetapi hal yang sama tidak dapat dikatakan untuk IComparable <T>. Dua alasan:

  1. Sudah ada sarana untuk membandingkan objek yang mungkin atau mungkin tidak tipe yang sama: Object.Equals. Karena IEquatable <T> tidak termasuk GetHashCode, perilakunya pada dasarnya harus sesuai dengan Object.Equals. Satu-satunya alasan untuk mengimplementasikan IEquatable <T> selain Object.Equals adalah kinerja. IEquatable <T> menawarkan peningkatan kinerja kecil versus Object.Equals ketika diterapkan pada jenis kelas yang disegel, dan peningkatan besar ketika diterapkan pada jenis struktur. Satu-satunya cara implementasi tipe uncsealed dari IEquatable <T> .Equals dapat memastikan bahwa perilakunya sesuai dengan Object.Equals yang mungkin di-override, bagaimanapun, adalah memanggil Object.Equals. Jika IEquatable <T> .Equals harus memanggil Object.Equals, semua keuntungan performa yang mungkin hilang.
  2. Kadang-kadang mungkin, bermakna, dan berguna, karena kelas dasar memiliki urutan alami yang ditentukan yang hanya melibatkan properti kelas dasar, yang akan konsisten melalui semua subkelas. Ketika memeriksa dua objek untuk kesetaraan, hasilnya tidak harus bergantung pada apakah seseorang menganggap objek sebagai tipe dasar atau tipe turunan. Ketika memerinci objek, bagaimanapun, hasilnya sering bergantung pada jenis yang digunakan sebagai dasar perbandingan. Objek kelas turunan harus mengimplementasikan IComparable <TheirOwnType> tetapi tidak boleh mengganti metode perbandingan tipe dasar. Hal ini sepenuhnya masuk akal untuk dua objek kelas turunan untuk dibandingkan sebagai "tidak beradab" bila dibandingkan dengan tipe induk, tetapi untuk satu untuk membandingkan di atas yang lain bila dibandingkan sebagai tipe turunan.

Implementasi IComparable non-generik dalam kelas yang diwariskan mungkin lebih dipertanyakan daripada implementasi IComparable <T>. Mungkin hal terbaik untuk dilakukan adalah memungkinkan kelas dasar untuk menerapkannya jika tidak diharapkan bahwa setiap kelas anak akan memerlukan beberapa pemesanan lainnya, tetapi untuk kelas anak tidak untuk mengimplementasi ulang atau mengesampingkan implementasi kelas induk.


4
2017-10-07 21:30



Paling Equals Implementasi Saya telah melihat memeriksa jenis objek yang dibandingkan, jika mereka tidak sama maka metode mengembalikan salah.

Ini dengan rapi menghindari masalah sub-tipe yang dibandingkan dengan tipe induknya, sehingga meniadakan kebutuhan untuk menyegel kelas.

Contoh nyata dari ini adalah mencoba membandingkan titik 2D (A) dengan titik 3D (B): untuk 2D, nilai x dan y dari titik 3D mungkin sama, tetapi untuk titik 3D, nilai z akan kemungkinan besar berbeda.

Ini artinya itu A == B akan benar, tapi B == A akan salah. Kebanyakan orang menyukai Equals operator menjadi komutatif, untuk memeriksa jenis jelas merupakan ide yang baik dalam kasus ini.

Tetapi bagaimana jika Anda subkelas dan Anda tidak menambahkan properti baru? Nah, itu sedikit lebih sulit untuk dijawab, dan mungkin tergantung pada situasi Anda.


1
2017-12-09 14:29