Cara terbaik untuk membandingkan dua objek kompleks

112

Saya memiliki dua objek kompleks seperti Object1dan 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?

Pengembang
sumber

Jawaban:

101

Terapkan IEquatable<T>(biasanya bersamaan dengan mengganti metode Object.Equalsdan yang diwariskan Object.GetHashCode) di semua jenis kustom Anda. Dalam kasus tipe komposit, panggil metode tipe yang terkandung Equalsdi dalam tipe yang memuat. Untuk koleksi yang terkandung, gunakan SequenceEqualmetode ekstensi, yang memanggil secara internal IEquatable<T>.Equalsatau Object.Equalspada 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 Equalsmetodenya. 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 Equalsmetodenya. Karena anggota kami diketik dengan benar, IEquatable<T>.Equalsmetode ini dipanggil secara langsung, melewati Object.Equalsmetode 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 Personkelas Anda memiliki PersonIdproperti, unik per orang di dunia nyata yang berbeda. Haruskah dua objek dengan nilai yang sama PersonIdtetapi berbeda Agedianggap 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.
Douglas
sumber
Saya mendapatkan objek ini melalui RIA Services ... Dapatkah saya menggunakan IEquatable <Foo> untuk objek tersebut dan dan mendapatkannya di bawah klien WPF?
Pengembang
1
Maksud Anda kelas dibuat secara otomatis? Saya belum pernah menggunakan Layanan RIA, tetapi saya akan berasumsi bahwa setiap kelas yang dihasilkan akan dideklarasikan sebagai partial- dalam hal ini, ya, Anda dapat menerapkan Equalsmetode mereka melalui deklarasi kelas parsial yang ditambahkan secara manual yang mereferensikan bidang / properti dari yang dibuat secara otomatis satu.
Douglas
Bagaimana jika "Alamat alamat" sebenarnya adalah "Alamat [] alamat", bagaimana cara penerapannya?
guiomie
2
Anda bisa memanggil LINQ Enumerable.SequenceEqualmetode pada array: this.Addresses.SequenceEqual(other.Addresses). Ini secara internal akan memanggil Address.Equalsmetode Anda untuk setiap pasangan alamat yang sesuai, mengingat bahwa Addresskelas mengimplementasikan IEquatable<Address>antarmuka.
Douglas
2
Kategori perbandingan lain yang mungkin diperiksa oleh pengembang adalah "WorksLike". Bagi saya, ini berarti bahwa meskipun dua instance mungkin memiliki beberapa nilai properti yang tidak sama, program akan menghasilkan hasil yang sama dari pemrosesan kedua instance tersebut.
John Kurtz
95

Serialisasi kedua objek dan bandingkan string yang dihasilkan

JoelFan
sumber
1
Saya tidak mengerti mengapa akan ada. Serialisasi biasanya merupakan proses yang dioptimalkan dan Anda perlu mengakses nilai setiap properti dalam hal apa pun.
JoelFan
5
Ada biaya yang sangat besar. Anda membuat aliran data, menambahkan string, lalu menguji persamaan string. Urutan besarnya, hanya itu. Belum lagi serialisasi akan menggunakan refleksi secara default.
Jerome Haltom
2
Aliran data bukanlah masalah besar, saya tidak mengerti mengapa Anda perlu menambahkan string ... menguji kesetaraan string adalah salah satu operasi yang paling dioptimalkan di luar sana .... Anda mungkin ada benarnya dengan refleksi ... tetapi pada seluruh serialisasi tidak akan menjadi "lipat besarnya" lebih buruk dari metode lain. Anda harus melakukan tolok ukur jika Anda mencurigai masalah kinerja ... Saya tidak mengalami masalah kinerja dengan metode ini
JoelFan
12
Saya +1ini 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.
Thomas
1
Itu bukan solusi yang baik karena kedua serialisasi mungkin salah dengan cara yang sama. Misalnya beberapa properti dari objek sumber mungkin belum serial dan ketika deserialized akan disetel ke null di objek tujuan. Dalam kasus seperti itu, pengujian Anda yang membandingkan string akan lulus, tetapi kedua objek sebenarnya tidak sama!
stackMeUp
35

Anda dapat menggunakan metode ekstensi, rekursi untuk menyelesaikan masalah ini:

public static bool DeepCompare(this object obj, object another)
{     
  if (ReferenceEquals(obj, another)) return true;
  if ((obj == null) || (another == null)) return false;
  //Compare two object's class, return false if they are difference
  if (obj.GetType() != another.GetType()) return false;

  var result = true;
  //Get all properties of obj
  //And compare each other
  foreach (var property in obj.GetType().GetProperties())
  {
      var objValue = property.GetValue(obj);
      var anotherValue = property.GetValue(another);
      if (!objValue.Equals(anotherValue)) result = false;
  }

  return result;
 }

public static bool CompareEx(this object obj, object another)
{
 if (ReferenceEquals(obj, another)) return true;
 if ((obj == null) || (another == null)) return false;
 if (obj.GetType() != another.GetType()) return false;

 //properties: int, double, DateTime, etc, not class
 if (!obj.GetType().IsClass) return obj.Equals(another);

 var result = true;
 foreach (var property in obj.GetType().GetProperties())
 {
    var objValue = property.GetValue(obj);
    var anotherValue = property.GetValue(another);
    //Recursion
    if (!objValue.DeepCompare(anotherValue))   result = false;
 }
 return result;
}

atau bandingkan dengan menggunakan Json (jika objek sangat kompleks) Anda dapat menggunakan Newtonsoft.Json:

public static bool JsonCompare(this object obj, object another)
{
  if (ReferenceEquals(obj, another)) return true;
  if ((obj == null) || (another == null)) return false;
  if (obj.GetType() != another.GetType()) return false;

  var objJson = JsonConvert.SerializeObject(obj);
  var anotherJson = JsonConvert.SerializeObject(another);

  return objJson == anotherJson;
}
Jonathan
sumber
1
Solusi pertama bagus! Saya suka bahwa Anda tidak perlu membuat serialisasi json atau menerapkan menambahkan kode apapun ke objek itu sendiri. Cocok jika Anda hanya membandingkan untuk pengujian unit. Bolehkah saya menyarankan menambahkan perbandingan sederhana dalam kasus objValue dan anotherValue sama-sama null? Ini akan menghindari munculnya NullReferenceException saat mencoba melakukan null.Equals () // ReSharper menonaktifkan sekali RedundantJumpStatement jika (objValue == anotherValue) melanjutkan; // null reference guard else if (! objValue.Equals (anotherValue)) Gagal (diharapkan, aktual);
Mark Conway
3
Apakah ada alasan untuk menggunakan DeepComparedaripada hanya menelepon CompareExsecara rekursif?
apostolov
3
Ini mungkin tidak perlu membandingkan seluruh struktur. Mengganti resultdengan return falseakan membuatnya lebih efisien.
Tim Sylvester
24

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

JotaBe
sumber
1
Saya hampir tidak berpikir bahwa IEquatable<T>penerapan memenuhi syarat sebagai kasus pengoptimalan prematur. Refleksi akan menjadi lebih lambat secara drastis. Implementasi default Equalsuntuk tipe nilai kustom tidak menggunakan refleksi; Microsoft sendiri menyarankan untuk menimpanya untuk performa: "Ganti Equalsmetode untuk jenis tertentu guna meningkatkan performa metode dan lebih dekat mewakili konsep kesetaraan untuk jenis tersebut".
Douglas
1
Itu tergantung berapa kali dia akan menjalankan metode sama dengan: 1, 10, 100, 100, satu juta? Itu akan membuat perbedaan besar. Jika dia dapat menggunakan solusi umum tanpa menerapkan apa pun, dia akan meluangkan waktu yang berharga. Jika terlalu lambat, maka inilah saatnya untuk mengimplementasikan IEquatable (dan mungkin bahkan mencoba membuat GetHashCode yang dapat disimpan dalam cache atau cerdas) Sejauh kecepatan Refleksi, saya harus setuju itu lebih lambat ... atau jauh lebih lambat, tergantung bagaimana Anda melakukannya ( yaitu menggunakan kembali Jenis PropertyInfos dan sebagainya, atau tidak).
JotaBe
@ Worthy7 Memang. Silakan, lihat isi proyek. Tes adalah cara yang baik untuk mendokumentasikan dengan contoh. Tapi, lebih baik dari itu, jika Anda mencarinya, Anda akan menemukan file bantuan .chm. Jadi proyek ini memiliki dokumentasi yang jauh lebih baik daripada kebanyakan proyek.
JotaBe
Maaf Anda benar, saya benar-benar merindukan tab "wiki". Saya terbiasa dengan semua orang yang menulis sesuatu di readme.
Layak7
9

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)

using System;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Text;

public static class MySerializer
{
    public static string Serialize(this object obj)
    {
        var serializer = new DataContractJsonSerializer(obj.GetType());
        using (var ms = new MemoryStream())
        {
            serializer.WriteObject(ms, obj);
            return Encoding.Default.GetString(ms.ToArray());
        }
    }
}

Setelah Anda mereferensikan kelas statis ini di file lain, Anda dapat melakukan ini:

Person p = new Person { Firstname = "Jason", LastName = "Argonauts" };
Person p2 = new Person { Firstname = "Jason", LastName = "Argonaut" };
//assuming you have already created a class person!
string personString = p.Serialize();
string person2String = p2.Serialize();

Sekarang Anda cukup menggunakan .Equals untuk membandingkannya. Saya menggunakan ini untuk memeriksa apakah objek ada dalam koleksi juga. Ini bekerja dengan sangat baik.

ozzy432836.dll
sumber
Bagaimana jika isi objek berupa array bilangan floating point. Sangat tidak efisien untuk mengonversinya menjadi string, dan konversi tersebut tunduk pada transformasi yang ditentukan di CultrureInfo. Ini hanya akan berfungsi jika sebagian besar data internal berupa string dan bilangan bulat. Kalau tidak, itu akan menjadi bencana.
John Alexiou
3
Bagaimana jika direktur baru menyuruh Anda merobek C # dan menggantinya dengan Python. Sebagai pengembang, kita perlu belajar bahwa pertanyaan bagaimana jika harus berhenti di suatu tempat. Pecahkan masalah, ke tahap berikutnya. Jika Anda PERNAH punya waktu, kembali ke sana ...
ozzy432836
2
Python lebih seperti MATLAB dalam sintaks dan penggunaan. Pasti ada alasan bagus untuk berpindah dari bahasa aman tipe statis ke skrip mishmash seperti python.
John Alexiou
5

Saya akan berasumsi bahwa Anda tidak mengacu pada objek yang sama secara harfiah

Object1 == Object2

Anda mungkin berpikir untuk melakukan perbandingan memori antara keduanya

memcmp(Object1, Object2, sizeof(Object.GetType())

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 tentukan Equalsmetode kustom untuk tipe Anda. Kemudian, dalam metode itu, uji manual setiap nilai. Tambahkan IEquatable<T>lagi pada tipe tertutup jika Anda bisa dan ulangi prosesnya.

class Foo : IEquatable<Foo>
{
  public bool Equals(Foo other)
  {
    /* check all the values */
    return false;
  }
}
payo
sumber
5

Serialisasi kedua objek, lalu hitung Kode Hash, lalu bandingkan.

Sk Shahnawaz-ul Haque
sumber
3

Saya menemukan fungsi di bawah ini untuk membandingkan objek.

 static bool Compare<T>(T Object1, T object2)
 {
      //Get the type of the object
      Type type = typeof(T);

      //return false if any of the object is false
      if (object.Equals(Object1, default(T)) || object.Equals(object2, default(T)))
         return false;

     //Loop through each properties inside class and get values for the property from both the objects and compare
     foreach (System.Reflection.PropertyInfo property in type.GetProperties())
     {
          if (property.Name != "ExtensionData")
          {
              string Object1Value = string.Empty;
              string Object2Value = string.Empty;
              if (type.GetProperty(property.Name).GetValue(Object1, null) != null)
                    Object1Value = type.GetProperty(property.Name).GetValue(Object1, null).ToString();
              if (type.GetProperty(property.Name).GetValue(object2, null) != null)
                    Object2Value = type.GetProperty(property.Name).GetValue(object2, null).ToString();
              if (Object1Value.Trim() != Object2Value.Trim())
              {
                  return false;
              }
          }
     }
     return true;
 }

Saya menggunakannya dan berfungsi dengan baik untuk saya.

Akshay
sumber
1
Yang pertama ifberarti Compare(null, null) == falsebukan itu yang saya harapkan.
Tim Sylvester
3

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

using System.IO;
using System.Xml.Serialization;

static class ObjectHelpers
{
    public static string SerializeObject<T>(this T toSerialize)
    {
        XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType());

        using (StringWriter textWriter = new StringWriter())
        {
            xmlSerializer.Serialize(textWriter, toSerialize);
            return textWriter.ToString();
        }
    }

    public static bool EqualTo(this object obj, object toCompare)
    {
        if (obj.SerializeObject() == toCompare.SerializeObject())
            return true;
        else
            return false;
    }

    public static bool IsBlank<T>(this T obj) where T: new()
    {
        T blank = new T();
        T newObj = ((T)obj);

        if (newObj.SerializeObject() == blank.SerializeObject())
            return true;
        else
            return false;
    }

}

Contoh Penggunaan

if (record.IsBlank())
    throw new Exception("Record found is blank.");

if (record.EqualTo(new record()))
    throw new Exception("Record found is blank.");
Arvo Bowen
sumber
2

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.

PaulG
sumber
2
Jika dan hanya jika mereka memberikan kelebihan muatan persamaan non-referensi yang Sama.
user7116
Setiap kelas harus menerapkan cara perbandingannya sendiri. Jika kelas pembuat tidak memiliki timpaan untuk metode Equals (), mereka akan menggunakan metode dasar kelas System.Object (), yang akan menyebabkan kesalahan dalam logika.
Dima
2
public class GetObjectsComparison
{
    public object FirstObject, SecondObject;
    public BindingFlags BindingFlagsConditions= BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;
}
public struct SetObjectsComparison
{
    public FieldInfo SecondObjectFieldInfo;
    public dynamic FirstObjectFieldInfoValue, SecondObjectFieldInfoValue;
    public bool ErrorFound;
    public GetObjectsComparison GetObjectsComparison;
}
private static bool ObjectsComparison(GetObjectsComparison GetObjectsComparison)
{
    GetObjectsComparison FunctionGet = GetObjectsComparison;
    SetObjectsComparison FunctionSet = new SetObjectsComparison();
    if (FunctionSet.ErrorFound==false)
        foreach (FieldInfo FirstObjectFieldInfo in FunctionGet.FirstObject.GetType().GetFields(FunctionGet.BindingFlagsConditions))
        {
            FunctionSet.SecondObjectFieldInfo =
            FunctionGet.SecondObject.GetType().GetField(FirstObjectFieldInfo.Name, FunctionGet.BindingFlagsConditions);

            FunctionSet.FirstObjectFieldInfoValue = FirstObjectFieldInfo.GetValue(FunctionGet.FirstObject);
            FunctionSet.SecondObjectFieldInfoValue = FunctionSet.SecondObjectFieldInfo.GetValue(FunctionGet.SecondObject);
            if (FirstObjectFieldInfo.FieldType.IsNested)
            {
                FunctionSet.GetObjectsComparison =
                new GetObjectsComparison()
                {
                    FirstObject = FunctionSet.FirstObjectFieldInfoValue
                    ,
                    SecondObject = FunctionSet.SecondObjectFieldInfoValue
                };

                if (!ObjectsComparison(FunctionSet.GetObjectsComparison))
                {
                    FunctionSet.ErrorFound = true;
                    break;
                }
            }
            else if (FunctionSet.FirstObjectFieldInfoValue != FunctionSet.SecondObjectFieldInfoValue)
            {
                FunctionSet.ErrorFound = true;
                break;
            }
        }
    return !FunctionSet.ErrorFound;
}
matan justme
sumber
Menggunakan prinsip Rekursi.
matan justme
1

Salah satu cara untuk melakukannya adalah dengan mengganti Equals()setiap jenis yang terlibat. Misalnya, objek tingkat atas Anda akan menimpa Equals()untuk memanggil Equals()metode dari semua 5 objek turunan. Semua objek tersebut juga harus diganti Equals(), dengan asumsi mereka adalah objek kustom, dan seterusnya hingga seluruh hierarki dapat dibandingkan hanya dengan melakukan pemeriksaan kesetaraan pada objek tingkat atas.

goric
sumber
1

Gunakan IEquatable<T>Antarmuka yang memiliki metode Equals.

Master Chief
sumber
1

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.

        /// <summary>Returns description of difference or empty value if equal</summary>
        public static string Compare(object obj1, object obj2, string path = "")
        {
            string path1 = string.IsNullOrEmpty(path) ? "" : path + ": ";
            if (obj1 == null && obj2 != null)
                return path1 + "null != not null";
            else if (obj2 == null && obj1 != null)
                return path1 + "not null != null";
            else if (obj1 == null && obj2 == null)
                return null;

            if (!obj1.GetType().Equals(obj2.GetType()))
                return "different types: " + obj1.GetType() + " and " + obj2.GetType();

            Type type = obj1.GetType();
            if (path == "")
                path = type.Name;

            if (type.IsPrimitive || typeof(string).Equals(type))
            {
                if (!obj1.Equals(obj2))
                    return path1 + "'" + obj1 + "' != '" + obj2 + "'";
                return null;
            }
            if (type.IsArray)
            {
                Array first = obj1 as Array;
                Array second = obj2 as Array;
                if (first.Length != second.Length)
                    return path1 + "array size differs (" + first.Length + " vs " + second.Length + ")";

                var en = first.GetEnumerator();
                int i = 0;
                while (en.MoveNext())
                {
                    string res = Compare(en.Current, second.GetValue(i), path);
                    if (res != null)
                        return res + " (Index " + i + ")";
                    i++;
                }
            }
            else if (typeof(System.Collections.IEnumerable).IsAssignableFrom(type))
            {
                System.Collections.IEnumerable first = obj1 as System.Collections.IEnumerable;
                System.Collections.IEnumerable second = obj2 as System.Collections.IEnumerable;

                var en = first.GetEnumerator();
                var en2 = second.GetEnumerator();
                int i = 0;
                while (en.MoveNext())
                {
                    if (!en2.MoveNext())
                        return path + ": enumerable size differs";

                    string res = Compare(en.Current, en2.Current, path);
                    if (res != null)
                        return res + " (Index " + i + ")";
                    i++;
                }
            }
            else
            {
                foreach (PropertyInfo pi in type.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public))
                {
                    try
                    {
                        var val = pi.GetValue(obj1);
                        var tval = pi.GetValue(obj2);
                        if (path.EndsWith("." + pi.Name))
                            return null;
                        var pathNew = (path.Length == 0 ? "" : path + ".") + pi.Name;
                        string res = Compare(val, tval, pathNew);
                        if (res != null)
                            return res;
                    }
                    catch (TargetParameterCountException)
                    {
                        //index property
                    }
                }
                foreach (FieldInfo fi in type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public))
                {
                    var val = fi.GetValue(obj1);
                    var tval = fi.GetValue(obj2);
                    if (path.EndsWith("." + fi.Name))
                        return null;
                    var pathNew = (path.Length == 0 ? "" : path + ".") + fi.Name;
                    string res = Compare(val, tval, pathNew);
                    if (res != null)
                        return res;
                }
            }
            return null;
        }

Untuk memudahkan penyalinan kode yang dibuat repositori

Alexey Obukhov
sumber
1

Sekarang Anda dapat menggunakan json.net. Buka saja Nuget dan pasang.

Dan Anda bisa melakukan sesuatu seperti ini:

    public bool Equals(SamplesItem sampleToCompare)
    {
        string myself = JsonConvert.SerializeObject(this);
        string other = JsonConvert.SerializeObject(sampleToCompare);

        return myself == other;
    }

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].

ashlar64
sumber
Jika Anda memiliki daftar di objek Anda dan yang memiliki daftar, maka mencoba melintasi kedua objek akan menjadi mimpi buruk. Jika Anda membuat serialisasi keduanya dan kemudian membandingkan Anda tidak perlu berurusan dengan skenario itu.
ashlar64
Jika objek kompleks Anda memiliki Dictionary di dalamnya, saya tidak percaya .net serializer dapat membuat serialisasi. Penyambung Json bisa.
ashlar64