İki karmaşık nesneyi karşılaştırmanın en iyi yolu


112

Ben gibi iki karmaşık nesneleri Object1ve Object2. Yaklaşık 5 seviyeli çocuk nesneleri var.

Aynı olup olmadıklarını söylemek için en hızlı yönteme ihtiyacım var.

Bu C # 4.0'da nasıl yapılabilir?

Yanıtlar:


101

Tüm özel türlerinize uygulayın IEquatable<T>(tipik olarak devralınan Object.Equalsve Object.GetHashCodeyöntemleri geçersiz kılmakla birlikte ). Bileşik türler durumunda, içerilen türlerin Equalsyöntemini içeren türler içinde çağırın . İçerilen koleksiyonlar için, SequenceEqualdahili olarak çağıran uzantı yöntemini kullanın IEquatable<T>.EqualsveyaObject.Equals her bir öğeyi . Bu yaklaşım tabii ki türlerinizin tanımlarını genişletmenizi gerektirecektir, ancak sonuçları serileştirmeyi içeren tüm genel çözümlerden daha hızlıdır.

Düzenleme : İşte üç seviyeli iç içe geçme ile uydurma bir örnek.

Değer türleri için, genellikle yalnızca Equalsyöntemlerini çağırabilirsiniz . Alanlar veya özellikler hiçbir zaman açıkça atanmamış olsa bile, yine de varsayılan bir değere sahip olacaklardır.

Referans türleri için, önce ReferenceEqualsreferans eşitliğini kontrol eden bir çağrı yapmalısınız - bu, aynı nesneye referans verdiğinizde bir verimlilik artışı olarak hizmet edecektir. Ayrıca, her iki başvurunun da boş olduğu durumları da ele alır. Bu kontrol başarısız olursa, örneğinizin alanının veya özelliğinin boş olmadığını (önlemek için NullReferenceException) onaylayın ve Equalsyöntemini çağırın . Üyelerimiz düzgün şekilde yazıldığından, IEquatable<T>.Equalsyöntem geçersiz kılınan Object.Equalsyöntemi atlayarak doğrudan çağrılır ( bu yöntemin yürütülmesi, türden dolayı marjinal olarak daha yavaş olacaktır).

Geçersiz kıldığınızda Object.Equals, geçersiz kılmanız da beklenir Object.GetHashCode; Bunu aşağıda özlülük uğruna yapmadım.

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);
    }
}

Güncelleme : Bu cevap birkaç yıl önce yazılmıştır. O zamandan beri, bu IEquality<T>tür senaryolar için değişken türler uygulamaktan uzaklaşmaya başladım . İki eşitlik kavramı vardır: özdeşlik ve denklik . Bellek temsili düzeyinde, bunlar popüler olarak "referans eşitliği" ve "değer eşitliği" olarak ayırt edilir (bkz. Eşitlik Karşılaştırmaları ). Bununla birlikte, aynı ayrım bir alan düzeyinde de geçerli olabilir. Sizin . Örneğin, a dolduruyorsanız, genellikle bir çağrının yalnızca argümanınızın kimliğini paylaşan var olan öğeleri döndürmesini beklersiniz , içeriği tamamen aynı olan eşdeğer öğeler değil. Bu kavram,PersonSınıfınızın her bir PersonIdgerçek dünya insanı için benzersiz bir özelliği . Aynı PersonIdancak farklı Agedeğerlere sahip iki nesne eşit mi yoksa farklı mı kabul edilmelidir? Yukarıdaki cevap, birinin denkliğin peşinde olduğunu varsayar. Ancak, birçok kullanım şekli vardır.IEquality<T>Bu tür uygulamaların sağladığını varsayan koleksiyonlar gibi arayüz kimlikHashSet<T>TryGetValue(T,T)GetHashCode:

Genel olarak, değiştirilebilir referans türleri için, GetHashCode()yalnızca aşağıdaki durumlarda geçersiz kılmalısınız :

  • Karma kodu değiştirilebilir olmayan alanlardan hesaplayabilirsiniz; veya
  • Değişken bir nesnenin karma kodunun, nesne karma koduna dayanan bir koleksiyonda yer alırken değişmemesini sağlayabilirsiniz.

Bu nesneyi RIA Hizmetleri aracılığıyla alıyorum ... Bu nesneler için IEquatable <Foo> kullanabilir miyim ve onu WPF istemcisi altına alabilir miyim?
Geliştirici

1
Sınıfların otomatik olarak oluşturulduğunu mu söylüyorsunuz? RIA Hizmetlerini kullanmadım, ancak oluşturulan tüm sınıfların şu şekilde bildirileceğini varsayıyorum partial- bu durumda, evet, Equalsyöntemlerini, otomatik olarak oluşturulan alanlara / özelliklere başvuran manuel olarak eklenen kısmi bir sınıf bildirimi aracılığıyla uygulayabilirsiniz. bir.
Douglas

Ya "Adres adresi" aslında "Adres [] adresler" ise, nasıl uygulanacaktır?
guiomie

2
Sen LINQ diyebiliriz Enumerable.SequenceEqualdiziler üzerinde yöntemini: this.Addresses.SequenceEqual(other.Addresses). Bu Address.Equals, Addresssınıfın IEquatable<Address>arabirimi uyguladığı göz önüne alındığında, her bir karşılık gelen adres çifti için yönteminizi dahili olarak çağırır .
Douglas

2
Bir geliştiricinin kontrol edebileceği başka bir karşılaştırma kategorisi "WorksLike" dir. Benim için bu, iki örneğin bazı eşit olmayan özellik değerlerine sahip olsalar bile, programın iki örneği işleyerek aynı sonucu üreteceği anlamına gelir.
John Kurtz

95

Her iki nesneyi de seri hale getirin ve ortaya çıkan dizeleri karşılaştırın


1
Neden olacağını anlamıyorum. Serileştirme normalde optimize edilmiş bir süreçtir ve her durumda her özelliğin değerine erişmeniz gerekir.
JoelFan

5
Çok büyük bir bedeli var. Bir veri akışı oluşturuyorsunuz, dizeler ekliyorsunuz ve ardından dize eşitliğini test ediyorsunuz. Büyüklük dereceleri, sadece bunun içinde. Serileştirmenin varsayılan olarak yansımayı kullanacağından bahsetmiyorum bile.
Jerome Haltom

2
Veri akışı önemli değil, neden dizeler eklemeniz gerektiğini anlamıyorum ... dize eşitliğini test etmek, piyasadaki en optimize işlemlerden biridir .... yansıtma konusunda bir noktaya sahip olabilirsiniz ... tüm serileştirme, diğer yöntemlerden daha kötü "büyüklük dereceleri" olmayacaktır. Performans sorunlarından şüpheleniyorsanız kıyaslama yapmalısınız ... Bu yöntemle performans sorunları yaşamadım
JoelFan

12
Bunu +1basitçe yapıyorum çünkü bu şekilde değer temelli bir eşitlik karşılaştırması yapmayı hiç düşünmemiştim. Güzel ve basit. Bu kodla bazı karşılaştırmalar görmek güzel olurdu.
Thomas

1
Her iki serileştirme de benzer şekilde ters gidebileceğinden bu iyi bir çözüm değildir. Örneğin, kaynak nesnenin bazı özellikleri serileştirilmemiş olabilir ve seri durumdan çıkarıldığında hedef nesnede null olarak ayarlanır. Böyle bir durumda, dizeleri karşılaştıran testiniz başarılı olacaktır, ancak her iki nesne de aslında aynı değildir!
stackMeUp

35

Bu sorunu çözmek için uzatma yöntemini, özyinelemeyi kullanabilirsiniz:

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;
}

veya Json kullanarak karşılaştırın (nesne çok karmaşıksa) Newtonsoft.Json'ı kullanabilirsiniz:

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;
}

1
İlk çözüm harika! Nesnelerin kendilerine herhangi bir kod eklemek veya json serileştirmek zorunda kalmamanızı seviyorum. Sadece birim testleri için karşılaştırma yaptığınızda uygundur. ObjValue ve anotherValue'nun her ikisinin de null'a eşit olması durumunda basit bir karşılaştırma eklemeyi önerebilir miyim? Bu, null.Equals () yapmaya çalışırken bir NullReferenceException oluşmasını önleyecektir // ReSharper, RedundantJumpStatement bir kez devre dışı bırakılırsa (objValue == anotherValue) continue; // boş referans koruması else if (! objValue.Equals (anotherValue)) Başarısız (beklenen, gerçek);
Mark Conway

3
DeepCompareBasitçe CompareExözyinelemeli aramak yerine kullanmak için herhangi bir sebep var mı ?
apostolov

3
Bu, tüm yapıyı gereksiz yere karşılaştırabilir. Değiştirme resultile return falsedaha verimli hale getirecektir.
Tim Sylvester

24

IEquatable'ı uygulamak istemiyorsanız, tüm özellikleri karşılaştırmak için her zaman Reflection'ı kullanabilirsiniz: - değer tipiyse, sadece karşılaştırın - referans tipiyse, "iç" özelliklerini karşılaştırmak için işlevi özyinelemeli olarak çağırın .

Performans hakkında değil, basitlik hakkında düşünüyorum. Bununla birlikte, nesnelerinizin tam tasarımına bağlıdır. Nesnenizin şekline bağlı olarak karmaşıklaşabilir (örneğin, özellikler arasında döngüsel bağımlılıklar varsa). Bununla birlikte, bunun gibi kullanabileceğiniz birkaç çözüm var:

Başka bir seçenek de nesneyi metin olarak serileştirmek, örneğin JSON.NET kullanarak ve serileştirme sonucunu karşılaştırmaktır. (JSON.NET, özellikler arasındaki Döngüsel bağımlılıkları işleyebilir).

En hızlı derken onu uygulamanın en hızlı yolunu mu yoksa hızlı çalışan bir kodu mu kastettiğinizi bilmiyorum. İhtiyacınız olup olmadığını bilmeden optimizasyon yapmamalısınız. Erken optimizasyon, tüm kötülüklerin köküdür


1
Bir IEquatable<T>uygulamanın erken optimizasyon durumu olarak nitelendirildiğini pek düşünmüyorum . Yansıma büyük ölçüde yavaşlayacak. EqualsÖzel değer türleri için varsayılan uygulaması yansıma kullanır; Microsoft, performans için onu geçersiz kılmayı öneriyor: " EqualsYöntemin performansını iyileştirmek ve tür için eşitlik kavramını daha yakından temsil etmek için belirli bir tür için yöntemi geçersiz kılın ."
Douglas

1
Eşittir yöntemini kaç kez çalıştıracağına bağlı: 1, 10, 100, 100, bir milyon? Bu büyük bir fark yaratacak. Herhangi bir şey uygulamadan jenerik bir çözüm kullanabilirse, biraz zaman ayıracaktır. Çok yavaşsa, IEquatable'ı uygulama zamanı (ve belki de önbelleğe alınabilir veya akıllı bir GetHashCode oluşturmaya bile çalışıyor) Yansıma hızına gelince, bunu nasıl yaptığınıza bağlı olarak daha yavaş veya çok daha yavaş olduğuna katılıyorum ( yani PropertyInfos Türlerini vb. yeniden kullanma veya kullanma).
JotaBe

@ Worthy7 Öyle. Lütfen proje içeriğine bakınız. Testler, örnek olarak belgelemenin iyi bir yoludur. Ama bundan daha iyisi, eğer ararsanız, bir .chm yardım dosyası bulacaksınız. Yani bu proje çoğu projeden çok daha iyi belgelere sahip.
JotaBe

Üzgünüm haklısın, "wiki" sekmesini tamamen kaçırdım. Herkesin benioku'da bir şeyler yazmasına alışkınım.
Worthy7

9

Her iki nesneyi de serileştirin ve elde edilen dizeleri @JoelFan ile karşılaştırın

Bunu yapmak için, buna benzer bir statik sınıf oluşturun ve TÜM nesneleri genişletmek için Uzantıları kullanın (böylece her türden nesne, koleksiyon, vb. Yönteme geçirebilirsiniz)

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());
        }
    }
}

Bu statik sınıfa başka bir dosyada başvurduğunuzda, şunu yapabilirsiniz:

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();

Şimdi bunları karşılaştırmak için .Equals kullanabilirsiniz. Bunu nesnelerin koleksiyonda olup olmadığını kontrol etmek için de kullanıyorum. Gerçekten iyi çalışıyor.


Ya nesnelerin içeriği kayan noktalı sayı dizileriyse? Bunları dizelere dönüştürmek çok verimsizdir ve dönüştürme, içinde tanımlanan dönüşümlere tabidir CultrureInfo. Bu, yalnızca dahili veriler çoğunlukla dizeler ve tam sayılardan oluşuyorsa işe yarar. Aksi takdirde felaket olur.
John Alexiou

3
Ya yeni bir yönetmen size C # 'ı söküp Python ile değiştirmenizi söylese? Geliştiriciler olarak, peki ya sorularının bir yerde durması gerektiğini öğrenmemiz gerekiyor. Sorunu çöz, bir sonrakine. HİÇ zamanınız olursa, ona geri dönün ...
ozzy432836

2
Python, sözdizimi ve kullanım açısından MATLAB'a daha çok benzer. Statik türden güvenli bir dilden python gibi karışık bir betiğe geçmek için gerçekten iyi bir neden olmalı.
John Alexiou

5

Kelimenin tam anlamıyla aynı nesnelerden bahsetmediğini varsayacağım

Object1 == Object2

İkisi arasında bir bellek karşılaştırması yapmayı düşünüyor olabilirsiniz

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

Ama bu c # :) için gerçek kod bile değil. Tüm verileriniz muhtemelen yığın üzerinde oluşturulduğundan, bellek bitişik değildir ve iki nesnenin eşitliğini agnostik bir şekilde karşılaştıramazsınız. Her değeri teker teker özel bir şekilde karşılaştırmanız gerekecek.

IEquatable<T>Arayüzü sınıfınıza eklemeyi düşünün ve Equalstürünüz için özel bir yöntem tanımlayın . Ardından, bu yöntemde her bir değeri manuel olarak test edin. IEquatable<T>Yapabiliyorsanız ekli türlerde tekrar ekleyin ve işlemi tekrarlayın.

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


3

Nesneleri karşılaştırmak için bu işlevi aşağıda buldum.

 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;
 }

Kullanıyorum ve benim için iyi çalışıyor.


1
İlki , beklediğim gibi olmayacağı ifanlamına geliyor Compare(null, null) == false.
Tim Sylvester

3

Burada zaten verilen birkaç cevaba dayanarak JoelFan'in cevabını çoğunlukla desteklemeye karar verdim . Uzatma yöntemlerini seviyorum ve bunlar, diğer çözümlerin hiçbiri karmaşık sınıflarımı karşılaştırmak için kullanmadığında benim için harika çalışıyor.

Uzatma Yöntemleri

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;
    }

}

Kullanım Örnekleri

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

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

2

Şunu söylemek isterim:

Object1.Equals(Object2)

aradığınız şey olurdu. Bu, nesnelerin aynı olup olmadığını görmek istiyorsan, sorduğun şey bu.

Tüm alt nesnelerin aynı olup olmadığını kontrol etmek istiyorsanız, bunları Equals()yöntemle bir döngüden geçirin .


2
Sadece ve ancak, eşitlik referans dışı eşitlik aşırı yüklemesi sağlarlarsa.
user7116

Her sınıf kendi karşılaştırma yöntemini uygulamalıdır. Yazarın sınıflarının Equals () yöntemi için geçersiz kılmaları yoksa, mantıkta hatalara yol açacak olan System.Object () sınıfının temel yöntemini kullanırlar.
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;
}

Özyineleme ilkelerini kullanmak.
matan justme

1

Bunu yapmanın bir yolu, Equals()dahil olan her türü geçersiz kılmak olacaktır . Örneğin, üst düzey nesneniz, 5 alt nesnenin tümünün yöntemini Equals()çağırmak için geçersiz kılar Equals(). Bu nesnelerin tümü Equals()de, özel nesneler oldukları varsayılarak geçersiz kılınmalıdır ve tüm hiyerarşi, üst düzey nesnelerde yalnızca bir eşitlik kontrolü gerçekleştirilerek karşılaştırılabilene kadar bu şekilde devam etmelidir .



1

Jonathan örneği sayesinde. Tüm durumlar için genişlettim (diziler, listeler, sözlükler, ilkel türler).

Bu, serileştirme olmadan bir karşılaştırmadır ve karşılaştırılan nesneler için herhangi bir arabirimin uygulanmasını gerektirmez.

        /// <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;
        }

Oluşturulan kod deposunun kolay kopyalanması için


1

Artık json.net'i kullanabilirsiniz. Sadece Nuget'e gidin ve kurun.

Ve bunun gibi bir şey yapabilirsiniz:

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

        return myself == other;
    }

Daha meraklı olmak istiyorsanız, belki nesne için bir genişletme yöntemi yapabilirsiniz. Lütfen bunun yalnızca kamusal mülkleri karşılaştırdığını unutmayın. Karşılaştırmayı yaptığınızda bir kamu malı görmezden gelmek isterseniz, [JsonIgnore] özelliğini kullanabilirsiniz.


Nesnelerinizde listeler varsa ve bunların listeleri varsa, her iki nesneyi de geçmeye çalışmak bir kabus olacaktır. Her ikisini de seri hale getirir ve sonra karşılaştırırsanız, bu senaryo ile uğraşmak zorunda kalmayacaksınız.
ashlar64

Karmaşık nesnenizde bir Sözlük varsa, .net serileştiricinin onu serileştirebileceğine inanmıyorum. Json serileştiricisi olabilir.
ashlar64
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.