Dua object Produk berisi Id dan Nama yang sama persis. Apakah keduanya sama? Untuk int, jawabannya jelas: 5 == 5 bernilai True. Untuk class, C# perlu kita beri tahu makna “sama”, karena jawaban bawaannya hanya membandingkan alamat memori.

Perilaku Bawaan: Perbandingan Reference #

Semua class mewarisi method Equals() dari class Object. Versi bawaannya mengembalikan True hanya bila kedua variable menunjuk object yang sama persis di heap. Operator == pada variable bertipe class berperilaku serupa. Dua object dengan isi identik tetap dianggap berbeda selama keduanya dibuat dua kali dengan new. Perbandingan seperti ini cocok bila object memang unik, misalnya object koneksi atau layanan tunggal; masalah muncul ketika object mewakili data yang nilainya bisa setara.

Mengoverride Equals dan GetHashCode #

Class Produk berikut mengganti makna kesetaraan menjadi perbandingan isi, dan sekaligus mengimplementasikan IEquatable<Produk>:

csharp
public class Produk : IEquatable<Produk>
{
    public int Id { get; set; }
    public string Nama { get; set; }

    public Produk(int id, string nama)
    {
        Id = id;
        Nama = nama;
    }

    public bool Equals(Produk? other)
    {
        if (other is null)
        {
            return false;
        }
        return Id == other.Id && Nama == other.Nama;
    }

    public override bool Equals(object? obj)
    {
        return Equals(obj as Produk);
    }

    public override int GetHashCode()
    {
        return HashCode.Combine(Id, Nama);
    }
}

Produk produkA = new Produk(1, "Kopi Gayo");
Produk produkB = new Produk(1, "Kopi Gayo");
Produk produkC = new Produk(2, "Teh Melati");

Console.WriteLine(produkA == produkB);
Console.WriteLine(produkA.Equals(produkB));
Console.WriteLine(produkA.Equals(produkC));
Console.WriteLine(produkA.GetHashCode() == produkB.GetHashCode());

Output yang Dihasilkan:

text
False
True
False
True

Mari kita bedah baris demi baris:

  1. : IEquatable<Produk>: Interface ini menuntut method Equals yang menerima parameter bertipe Produk, sehingga pembanding bekerja langsung pada tipe yang tepat tanpa cast.
  2. public bool Equals(Produk? other): Tanda ? menyatakan parameter boleh null; bila bernilai null, method mengembalikan False.
  3. return Id == other.Id && Nama == other.Nama;: Kesetaraan didefinisikan dari isi, bukan alamat; produkA dan produkB dianggap sama meskipun keduanya dibuat dengan new.
  4. public override bool Equals(object? obj): Versi lama dari Object didelegasikan ke versi bertipe kuat agar perilaku tetap konsisten di semua jalur pemanggilan.
  5. public override int GetHashCode(): Method ini menghasilkan angka ringkas dari isi object; dua object yang Equals-nya True wajib punya kode yang sama.
  6. produkA == produkB: Hasilnya False karena operator == tetap membandingkan reference.

Kontrak yang perlu diingat: Equals yang mengembalikan True menuntut GetHashCode yang sama, dan perbandingan isi sebaiknya diterapkan pada kedua method sekaligus agar koleksi seperti Dictionary bekerja dengan benar.

Ringkasan alat perbandingan yang dipakai pada bab ini:

AlatMembandingkanCatatan
==Reference untuk classPerlu operator overload agar membandingkan isi
Equals(object?)Sesuai versi overrideDidelegasikan ke IEquatable<T> pada contoh
IEquatable<T>.EqualsIsi object bertipe samaVersi bertipe kuat, tanpa cast
GetHashCode()Angka ringkas dari isiWajib konsisten dengan Equals