Apa perbedaan antara objek IEquatable dan hanya mengesampingkan.Equals ()?


185

Saya ingin Foodkelas saya dapat menguji setiap kali itu sama dengan contoh lain dari Food. Saya nantinya akan menggunakannya melawan Daftar, dan saya ingin menggunakan List.Contains()metodenya. Haruskah saya menerapkan IEquatable<Food>atau hanya menimpanya Object.Equals()? Dari MSDN:

Metode ini menentukan kesetaraan dengan menggunakan pembanding kesetaraan default, seperti yang didefinisikan oleh implementasi objek dari metode IEquatable.Equals untuk T (jenis nilai dalam daftar).

Jadi pertanyaan saya berikutnya adalah: fungsi / kelas mana dari kerangka NET yang digunakan Object.Equals()? Haruskah saya menggunakannya di tempat pertama?


3
Sebuah penjelasan yang sangat baik di sini blogs.msdn.com/b/jaredpar/archive/2009/01/15/...
Naufal

kemungkinan duplikat dari Understanding IEquatable
nawfal

Jawaban:


214

Alasan utamanya adalah kinerja. Ketika obat generik diperkenalkan di NET 2.0 mereka dapat menambahkan sekelompok kelas rapi seperti List<T>, Dictionary<K,V>, HashSet<T>, dll Struktur ini menggunakan berat GetHashCodedan Equals. Tetapi untuk tipe nilai ini diperlukan tinju. IEquatable<T>memungkinkan struktur menerapkan metode yang sangat diketik Equalssehingga tidak ada tinju yang diperlukan. Dengan demikian kinerjanya jauh lebih baik ketika menggunakan tipe nilai dengan koleksi generik.

Jenis referensi tidak mendapat manfaat sebanyak tetapi IEquatable<T>implementasi tidak membiarkan Anda menghindari para pemain System.Objectyang dapat membuat perbedaan jika sering dipanggil.

Sebagaimana dicatat di blog Jared Parson , Anda masih harus mengimplementasikan penggantian Objek.


Apakah ada pemeran di antara tipe referensi? Saya selalu berpikir bahwa gips hanyalah "pernyataan" yang Anda buat untuk kompiler ketika Anda menetapkan beberapa gips yang tidak jelas dari satu jenis objek ke objek lainnya. Ini, setelah Anda kompilasi, kode itu bahkan tidak akan tahu ada pemain di sana.
melahap elysium

7
Itu benar dalam C ++ tetapi tidak bahasa .NET yang memberlakukan keamanan jenis. Ada cast runtime dan jika cast tidak berhasil, pengecualian dilemparkan. Jadi ada penalti runtime kecil untuk membayar casting. Kompiler dapat mengoptimalkan upcast yang jauh Misalnya objek o = (objek) "string"; Tapi downcasting - string s = (string) o; - harus terjadi saat runtime.
Josh

1
Saya melihat. Secara kebetulan Anda memiliki tempat di mana saya bisa mendapatkan informasi "lebih dalam" tentang .NET? Terima kasih!
melahap elysium

7
Saya akan merekomendasikan CLR via C # oleh Jeff Richter dan C # in Depth oleh Jon Skeet. Adapun blog, blog Wintellect baik, blog msdn, dll.
Josh

Apakah IEquatable<T>antarmuka melakukan sesuatu selain mengingatkan pengembang untuk memasukkan public bool Equals(T other) anggota dalam kelas atau struct? Ada atau tidak adanya antarmuka tidak membuat perbedaan saat dijalankan. Kelebihan Equalsakan tampaknya semua yang diperlukan.
mikemay

48

Menurut MSDN :

Jika Anda menerapkan IEquatable<T>, Anda juga harus menimpa implementasi kelas dasar Object.Equals(Object)dan GetHashCode agar perilaku mereka konsisten dengan IEquatable<T>.Equals metode tersebut. Jika Anda melakukan override Object.Equals(Object), implementasi Anda yang ditimpa juga dipanggil dalam panggilan ke Equals(System.Object, System.Object)metode statis di kelas Anda. Ini memastikan bahwa semua doa Equalsmetode mengembalikan hasil yang konsisten.

Jadi sepertinya tidak ada perbedaan fungsional yang nyata antara keduanya kecuali bahwa keduanya dapat dipanggil tergantung pada bagaimana kelas digunakan. Dari sudut pandang kinerja, lebih baik menggunakan versi generik karena tidak ada penalti tinju / unboxing yang terkait dengannya.

Dari sudut pandang logis, lebih baik untuk mengimplementasikan antarmuka. Meng-override objek tidak benar-benar memberi tahu siapa pun bahwa kelas Anda sebenarnya setara. Override mungkin hanya kelas do nothing atau implementasi dangkal. Menggunakan antarmuka secara eksplisit mengatakan, "Hei, benda ini valid untuk pemeriksaan kesetaraan!" Itu hanya desain yang lebih baik.


9
Structs pasti harus menerapkan iEquatable (ofOwnType mereka) jika mereka akan digunakan sebagai kunci dalam Kamus atau koleksi serupa; itu akan menawarkan peningkatan kinerja utama. Kelas yang tidak dapat diwariskan akan menerima sedikit peningkatan kinerja dengan mengimplementasikan IEquatable (dari yourOwnType). Kelas yang dapat diwarisi seharusnya // tidak // mengimplementasikan IEquatable.
supercat

30

Memperluas apa yang dikatakan Josh dengan contoh praktis. +1 ke Josh - Saya akan menulis hal yang sama dalam jawaban saya.

public abstract class EntityBase : IEquatable<EntityBase>
{
    public EntityBase() { }

    #region IEquatable<EntityBase> Members

    public bool Equals(EntityBase other)
    {
        //Generic implementation of equality using reflection on derived class instance.
        return true;
    }

    public override bool Equals(object obj)
    {
        return this.Equals(obj as EntityBase);
    }

    #endregion
}

public class Author : EntityBase
{
    public Author() { }
}

public class Book : EntityBase
{
    public Book() { }
}

Dengan cara ini, saya memiliki metode Equals () yang dapat digunakan kembali yang bekerja di luar kotak untuk semua kelas turunan saya.


Hanya satu pertanyaan lagi. Apa keuntungan menggunakan "obj sebagai EntityBase" daripada obj (EntityBase)? Hanya masalah gaya atau apakah ada keuntungan sama sekali?
melahap elysium

22
dalam kasus "obj sebagai EntityBase" - jika obj bukan dari tipe EntityBase, itu akan melewati "null" dan berlanjut tanpa kesalahan atau pengecualian, Tetapi dalam kasus "(EntityBase) obj", ia akan dengan paksa mencoba untuk melemparkan objek ke EntityBase dan jika objek bukan dari tipe EntityBase, itu akan melempar InvalidCastException. Dan ya, "as" hanya bisa diterapkan ke tipe referensi.
ini. __curious_geek

1
Tautan Josh ke blog Jared Par tampaknya menyarankan Anda juga perlu mengganti GetHashCode. Bukankah ini masalahnya?
Amicable

3
Saya tidak benar-benar mendapatkan nilai ekstra yang diberikan implementasi Anda. Bisakah Anda mengklarifikasi masalah yang dipecahkan oleh kelas dasar abstrak Anda?
Mert Akcakaya

1
@Amicable - ya, setiap kali Anda menimpa Object.Equals (Obyek), Anda juga harus menimpa GetHashCode agar wadah berfungsi.
namford

0

Jika kita menyebutnya object.Equals, itu memaksa untuk melakukan tinju mahal pada tipe nilai. Ini tidak diinginkan dalam skenario yang peka terhadap kinerja. Solusinya adalah menggunakan IEquatable<T>.

public interface IEquatable<T>
{
  bool Equals (T other);
}

Gagasan di belakang IEquatable<T>adalah bahwa ia memberikan hasil yang sama object.Equalstetapi lebih cepat. Batasan where T : IEquatable<T>harus digunakan dengan tipe generik seperti di bawah ini.

public class Test<T> where T : IEquatable<T>
{
  public bool IsEqual (T a, T b)
  {
    return a.Equals (b); // No boxing with generic T
  }
}

jika tidak, itu mengikat slower object.Equals().

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.