Saya memiliki dua objek kompleks seperti Object1
dan Object2
. Mereka memiliki sekitar 5 tingkat objek anak.
Saya membutuhkan metode tercepat untuk mengatakan apakah mereka sama atau tidak.
Bagaimana ini bisa dilakukan di C # 4.0?
Terapkan IEquatable<T>
(biasanya bersamaan dengan mengganti metode Object.Equals
dan yang diwariskan Object.GetHashCode
) di semua jenis kustom Anda. Dalam kasus tipe komposit, panggil metode tipe yang terkandung Equals
di dalam tipe yang memuat. Untuk koleksi yang terkandung, gunakan SequenceEqual
metode ekstensi, yang memanggil secara internal IEquatable<T>.Equals
atau Object.Equals
pada setiap elemen. Pendekatan ini jelas akan meminta Anda untuk memperluas definisi tipe Anda, tetapi hasilnya lebih cepat daripada solusi umum yang melibatkan serialisasi.
Sunting : Berikut adalah contoh yang dibuat-buat dengan tiga tingkat bersarang.
Untuk tipe nilai, Anda biasanya dapat memanggil Equals
metodenya. Meskipun bidang atau properti tidak pernah ditetapkan secara eksplisit, mereka akan tetap memiliki nilai default.
Untuk jenis referensi, Anda harus memanggil terlebih dahulu ReferenceEquals
, yang memeriksa persamaan referensi - ini akan berfungsi sebagai peningkatan efisiensi saat Anda kebetulan merujuk objek yang sama. Ini juga akan menangani kasus di mana kedua referensi bernilai null. Jika pemeriksaan itu gagal, konfirmasikan bahwa bidang atau properti instance Anda bukan null (untuk dihindari NullReferenceException
) dan panggil Equals
metodenya. Karena anggota kami diketik dengan benar, IEquatable<T>.Equals
metode ini dipanggil secara langsung, melewati Object.Equals
metode yang ditimpa (yang eksekusinya akan sedikit lebih lambat karena tipe cast).
Saat Anda mengganti Object.Equals
, Anda juga diharapkan untuk mengganti Object.GetHashCode
; Saya tidak melakukannya di bawah ini demi keringkasan.
public class Person : IEquatable<Person>
{
public int Age { get; set; }
public string FirstName { get; set; }
public Address Address { get; set; }
public override bool Equals(object obj)
{
return this.Equals(obj as Person);
}
public bool Equals(Person other)
{
if (other == null)
return false;
return this.Age.Equals(other.Age) &&
(
object.ReferenceEquals(this.FirstName, other.FirstName) ||
this.FirstName != null &&
this.FirstName.Equals(other.FirstName)
) &&
(
object.ReferenceEquals(this.Address, other.Address) ||
this.Address != null &&
this.Address.Equals(other.Address)
);
}
}
public class Address : IEquatable<Address>
{
public int HouseNo { get; set; }
public string Street { get; set; }
public City City { get; set; }
public override bool Equals(object obj)
{
return this.Equals(obj as Address);
}
public bool Equals(Address other)
{
if (other == null)
return false;
return this.HouseNo.Equals(other.HouseNo) &&
(
object.ReferenceEquals(this.Street, other.Street) ||
this.Street != null &&
this.Street.Equals(other.Street)
) &&
(
object.ReferenceEquals(this.City, other.City) ||
this.City != null &&
this.City.Equals(other.City)
);
}
}
public class City : IEquatable<City>
{
public string Name { get; set; }
public override bool Equals(object obj)
{
return this.Equals(obj as City);
}
public bool Equals(City other)
{
if (other == null)
return false;
return
object.ReferenceEquals(this.Name, other.Name) ||
this.Name != null &&
this.Name.Equals(other.Name);
}
}
Pembaruan : Jawaban ini ditulis beberapa tahun yang lalu. Sejak itu, saya mulai menjauh dari penerapan IEquality<T>
tipe yang bisa berubah untuk skenario semacam itu. Ada dua pengertian tentang persamaan: identitas dan kesetaraan . Pada tingkat representasi memori, ini secara populer dibedakan sebagai "persamaan referensi" dan "persamaan nilai" (lihat Perbandingan Kesetaraan ). Namun, perbedaan yang sama juga dapat diterapkan di tingkat domain. Misalkan Person
kelas Anda memiliki PersonId
properti, unik per orang di dunia nyata yang berbeda. Haruskah dua objek dengan nilai yang sama PersonId
tetapi berbeda Age
dianggap sama atau berbeda? Jawaban di atas mengasumsikan bahwa seseorang berada setelah persamaan. Namun, ada banyak penggunaan fileIEquality<T>
antarmuka, seperti koleksi, yang mengasumsikan bahwa implementasi tersebut menyediakan identitas . Misalnya, jika Anda mengisi a HashSet<T>
, Anda biasanya mengharapkan TryGetValue(T,T)
panggilan untuk mengembalikan elemen yang ada yang hanya berbagi identitas argumen Anda, tidak harus elemen setara yang isinya benar-benar sama. Gagasan ini diperkuat oleh catatan di GetHashCode
:
Secara umum, untuk jenis referensi yang bisa berubah, Anda harus mengganti
GetHashCode()
hanya jika:
- Anda dapat menghitung kode hash dari bidang yang tidak bisa berubah; atau
- Anda dapat memastikan bahwa kode hash dari objek yang bisa berubah tidak berubah saat objek tersebut berada dalam koleksi yang bergantung pada kode hashnya.
partial
- dalam hal ini, ya, Anda dapat menerapkanEquals
metode mereka melalui deklarasi kelas parsial yang ditambahkan secara manual yang mereferensikan bidang / properti dari yang dibuat secara otomatis satu.Enumerable.SequenceEqual
metode pada array:this.Addresses.SequenceEqual(other.Addresses)
. Ini secara internal akan memanggilAddress.Equals
metode Anda untuk setiap pasangan alamat yang sesuai, mengingat bahwaAddress
kelas mengimplementasikanIEquatable<Address>
antarmuka.Serialisasi kedua objek dan bandingkan string yang dihasilkan
sumber
+1
ini hanya karena saya tidak pernah berpikir untuk melakukan perbandingan kesetaraan berbasis nilai dengan cara ini. Itu bagus dan sederhana. Akan rapi untuk melihat beberapa tolok ukur dengan kode ini.Anda dapat menggunakan metode ekstensi, rekursi untuk menyelesaikan masalah ini:
atau bandingkan dengan menggunakan Json (jika objek sangat kompleks) Anda dapat menggunakan Newtonsoft.Json:
sumber
DeepCompare
daripada hanya meneleponCompareEx
secara rekursif?result
denganreturn false
akan membuatnya lebih efisien.Jika Anda tidak ingin mengimplementasikan IEquatable, Anda selalu dapat menggunakan Reflection untuk membandingkan semua properti: - jika mereka adalah tipe nilai, bandingkan saja -jika mereka adalah tipe referensi, panggil fungsi secara rekursif untuk membandingkan properti "dalam" -nya .
Saya tidak berpikir tentang performa, tapi tentang kesederhanaan. Itu tergantung, bagaimanapun pada desain yang tepat dari objek Anda. Ini bisa menjadi rumit tergantung pada bentuk objek Anda (misalnya jika ada ketergantungan siklik antar properti). Namun, ada beberapa solusi di luar sana yang dapat Anda gunakan, seperti ini:
Pilihan lainnya adalah menserialkan objek sebagai teks, misalnya menggunakan JSON.NET, dan membandingkan hasil serialisasi. (JSON.NET dapat menangani dependensi Cyclic antar properti).
Saya tidak tahu apakah yang Anda maksud tercepat adalah cara tercepat untuk menerapkannya atau kode yang berjalan cepat. Anda tidak harus mengoptimalkan sebelum mengetahui jika Anda perlu. Optimalisasi dini adalah akar dari segala kejahatan
sumber
IEquatable<T>
penerapan memenuhi syarat sebagai kasus pengoptimalan prematur. Refleksi akan menjadi lebih lambat secara drastis. Implementasi defaultEquals
untuk tipe nilai kustom tidak menggunakan refleksi; Microsoft sendiri menyarankan untuk menimpanya untuk performa: "GantiEquals
metode untuk jenis tertentu guna meningkatkan performa metode dan lebih dekat mewakili konsep kesetaraan untuk jenis tersebut".Serialisasi kedua objek dan bandingkan string yang dihasilkan dengan @JoelFan
Jadi untuk melakukan ini, buat kelas statis seperti itu dan gunakan Ekstensi untuk memperluas SEMUA objek (sehingga Anda dapat meneruskan semua jenis objek, koleksi, dll ke dalam metode)
Setelah Anda mereferensikan kelas statis ini di file lain, Anda dapat melakukan ini:
Sekarang Anda cukup menggunakan .Equals untuk membandingkannya. Saya menggunakan ini untuk memeriksa apakah objek ada dalam koleksi juga. Ini bekerja dengan sangat baik.
sumber
CultrureInfo
. Ini hanya akan berfungsi jika sebagian besar data internal berupa string dan bilangan bulat. Kalau tidak, itu akan menjadi bencana.Saya akan berasumsi bahwa Anda tidak mengacu pada objek yang sama secara harfiah
Anda mungkin berpikir untuk melakukan perbandingan memori antara keduanya
Tapi itu bahkan bukan kode sebenarnya di c # :). Karena semua data Anda mungkin dibuat di heap, memori tidak bersebelahan dan Anda tidak bisa begitu saja membandingkan kesetaraan dua objek secara agnostik. Anda harus membandingkan setiap nilai, satu per satu, dengan cara khusus.
Pertimbangkan untuk menambahkan
IEquatable<T>
antarmuka ke kelas Anda, dan tentukanEquals
metode kustom untuk tipe Anda. Kemudian, dalam metode itu, uji manual setiap nilai. TambahkanIEquatable<T>
lagi pada tipe tertutup jika Anda bisa dan ulangi prosesnya.sumber
Serialisasi kedua objek, lalu hitung Kode Hash, lalu bandingkan.
sumber
Saya menemukan fungsi di bawah ini untuk membandingkan objek.
Saya menggunakannya dan berfungsi dengan baik untuk saya.
sumber
if
berartiCompare(null, null) == false
bukan itu yang saya harapkan.Berdasarkan beberapa jawaban yang sudah diberikan di sini, saya memutuskan untuk mendukung sebagian besar jawaban JoelFan . Saya suka metode ekstensi dan ini telah bekerja dengan baik untuk saya ketika tidak ada solusi lain yang akan menggunakannya untuk membandingkan kelas kompleks saya.
Metode Ekstensi
Contoh Penggunaan
sumber
Saya akan mengatakan bahwa:
Object1.Equals(Object2)
akan menjadi apa yang Anda cari. Itu jika Anda ingin melihat apakah objeknya sama, yang sepertinya Anda tanyakan.
Jika Anda ingin memeriksa apakah semua objek turunannya sama, jalankan melalui loop dengan
Equals()
metode.sumber
sumber
Salah satu cara untuk melakukannya adalah dengan mengganti
Equals()
setiap jenis yang terlibat. Misalnya, objek tingkat atas Anda akan menimpaEquals()
untuk memanggilEquals()
metode dari semua 5 objek turunan. Semua objek tersebut juga harus digantiEquals()
, dengan asumsi mereka adalah objek kustom, dan seterusnya hingga seluruh hierarki dapat dibandingkan hanya dengan melakukan pemeriksaan kesetaraan pada objek tingkat atas.sumber
Gunakan
IEquatable<T>
Antarmuka yang memiliki metodeEquals
.sumber
Berkat teladan Jonathan. Saya mengembangkannya untuk semua kasus (array, daftar, kamus, tipe primitif).
Ini adalah perbandingan tanpa serialisasi dan tidak memerlukan implementasi antarmuka apa pun untuk objek yang dibandingkan.
Untuk memudahkan penyalinan kode yang dibuat repositori
sumber
Sekarang Anda dapat menggunakan json.net. Buka saja Nuget dan pasang.
Dan Anda bisa melakukan sesuatu seperti ini:
Anda mungkin bisa membuat metode ekstensi untuk objek jika Anda ingin menjadi lebih menarik. Harap dicatat ini hanya membandingkan properti publik. Dan jika Anda ingin mengabaikan properti publik saat melakukan perbandingan, Anda dapat menggunakan atribut [JsonIgnore].
sumber