Il modo migliore per confrontare due oggetti complessi


112

Ho due oggetti complessi come Object1e Object2. Hanno circa 5 livelli di oggetti figlio.

Ho bisogno del metodo più veloce per dire se sono uguali o no.

Come è possibile farlo in C # 4.0?

Risposte:


101

Implementa IEquatable<T>(in genere in combinazione con l'override dei metodi Object.Equalse ereditati Object.GetHashCode) su tutti i tipi personalizzati. Nel caso di tipi compositi, richiamare il Equalsmetodo dei tipi contenuti all'interno dei tipi contenenti. Per le raccolte contenute, utilizzare il SequenceEqualmetodo di estensione, che chiama internamente IEquatable<T>.Equalso Object.Equalssu ogni elemento. Questo approccio richiederà ovviamente di estendere le definizioni dei tipi, ma i suoi risultati sono più veloci di qualsiasi soluzione generica che coinvolge la serializzazione.

Modifica : ecco un esempio artificioso con tre livelli di nidificazione.

Per i tipi di valore, in genere puoi semplicemente chiamare il loro Equalsmetodo. Anche se i campi o le proprietà non sono mai stati assegnati in modo esplicito, avrebbero comunque un valore predefinito.

Per i tipi di riferimento, dovresti prima chiamare ReferenceEquals, che verifica l'uguaglianza dei riferimenti: questo servirebbe come un aumento dell'efficienza quando ti capita di fare riferimento allo stesso oggetto. Gestirà anche i casi in cui entrambi i riferimenti sono nulli. Se il controllo fallisce, conferma che il campo o la proprietà dell'istanza non è nullo (per evitare NullReferenceException) e chiama il suo Equalsmetodo. Poiché i nostri membri sono digitati correttamente, il IEquatable<T>.Equalsmetodo viene chiamato direttamente, ignorando il Object.Equalsmetodo sovrascritto (la cui esecuzione sarebbe leggermente più lenta a causa del cast del tipo).

Quando esegui l'override Object.Equals, devi anche eseguire l'override Object.GetHashCode; Non l'ho fatto di seguito per motivi di concisione.

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

Aggiornamento : questa risposta è stata scritta diversi anni fa. Da allora, ho iniziato ad allontanarmi dall'implementazione IEquality<T>di tipi mutabili per tali scenari. Esistono due nozioni di uguaglianza: identità ed equivalenza . A livello di rappresentazione della memoria, questi sono comunemente distinti come "uguaglianza di riferimento" e "uguaglianza di valore" (vedere Confronti di uguaglianza ). Tuttavia, la stessa distinzione può essere applicata anche a livello di dominio. Supponi che la tua Personclasse abbia una PersonIdproprietà, unica per persona distinta del mondo reale. Due oggetti con valori uguali PersonIdma diversi Agedevono essere considerati uguali o diversi? La risposta sopra presuppone che uno sia dopo l'equivalenza. Tuttavia, ci sono molti utilizzi diIEquality<T>interfaccia, come le raccolte, che presuppongono che tali implementazioni forniscano l' identità . Ad esempio, se stai popolando un HashSet<T>, in genere ti aspetteresti che una TryGetValue(T,T)chiamata restituisca elementi esistenti che condividono semplicemente l'identità del tuo argomento, non necessariamente elementi equivalenti il ​​cui contenuto è completamente lo stesso. Questa nozione è rafforzata dalle note su GetHashCode:

In generale, per i tipi di riferimento modificabili, è necessario eseguire l'override GetHashCode()solo se:

  • È possibile calcolare il codice hash da campi che non sono modificabili; o
  • Puoi assicurarti che il codice hash di un oggetto modificabile non cambi mentre l'oggetto è contenuto in una raccolta che si basa sul suo codice hash.

Ottengo questo oggetto tramite i servizi RIA ... Posso usare IEquatable <Foo> per quegli oggetti e ottenerlo nel client WPF?
Sviluppatore

1
Vuoi dire che le classi sono generate automaticamente? Non ho utilizzato i servizi RIA, ma presumo che qualsiasi classe generata verrebbe dichiarata come partial- nel qual caso, sì, puoi implementare il loro Equalsmetodo attraverso una dichiarazione di classe parziale aggiunta manualmente che fa riferimento a campi / proprietà dall'auto-generata uno.
Douglas

E se "Indirizzo indirizzo" fosse effettivamente "Indirizzo [] indirizzi", come sarebbe implementato?
guiomie

2
Si può chiamare l'LINQ Enumerable.SequenceEqualmetodo su array: this.Addresses.SequenceEqual(other.Addresses). Questo chiamerebbe internamente il tuo Address.Equalsmetodo per ogni coppia di indirizzi corrispondenti, dato che la Addressclasse implementa l' IEquatable<Address>interfaccia.
Douglas

2
Un'altra categoria di confronto che uno sviluppatore potrebbe verificare è "WorksLike". Per me, questo significa che anche se due istanze potrebbero avere alcuni valori di proprietà disuguali, il programma produrrà lo stesso risultato dall'elaborazione delle due istanze.
John Kurtz

95

Serializza entrambi gli oggetti e confronta le stringhe risultanti


1
Non vedo perché dovrebbe esserci. La serializzazione è normalmente un processo ottimizzato e in ogni caso è necessario accedere al valore di ogni proprietà.
JoelFan

5
C'è un costo enorme. Stai generando un flusso di dati, aggiungendo stringhe e quindi testando l'uguaglianza delle stringhe. Ordini di grandezza, proprio in questo. Per non parlare della serializzazione che utilizzerà la riflessione per impostazione predefinita.
Jerome Haltom

2
Il flusso di dati non è un grosso problema, non vedo perché dovresti aggiungere stringhe ... testare l'uguaglianza delle stringhe è una delle operazioni più ottimizzate là fuori ... potresti avere un punto con la riflessione ... ma su l'intera serializzazione non sarà "ordini di grandezza" peggiore di altri metodi. Dovresti fare benchmark se sospetti problemi di prestazioni ... Non ho riscontrato problemi di prestazioni con questo metodo
JoelFan

12
Lo sono +1semplicemente perché non ho mai pensato di fare un confronto di uguaglianza basato sui valori in questo modo. È carino e semplice. Sarebbe bello vedere alcuni benchmark con questo codice.
Thomas

1
Questa non è una buona soluzione in quanto entrambe le serializzazioni potrebbero andare storte in modo simile. Ad esempio, alcune proprietà dell'oggetto di origine potrebbero non essere state serializzate e quando deserializzate verranno impostate su null nell'oggetto di destinazione. In tal caso, il test che confronta le stringhe passerà, ma entrambi gli oggetti in realtà non sono uguali!
stackMeUp

35

Puoi utilizzare il metodo di estensione, la ricorsione per risolvere questo problema:

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

o confronta usando Json (se l'oggetto è molto complesso) Puoi usare 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;
}

1
La prima soluzione è ottima! Mi piace che non sia necessario serializzare json o implementare l'aggiunta di codice agli oggetti stessi. Adatto quando stai solo confrontando per unit test. Potrei suggerire di aggiungere un semplice confronto nel caso in cui objValue e anotherValue siano entrambi uguali a null? Ciò eviterà che venga generata un'eccezione NullReferenceException quando si tenta di eseguire null.Equals () // ReSharper disabilita una volta RedundantJumpStatement if (objValue == anotherValue) continue; // guardia di riferimento nullo else if (! objValue.Equals (anotherValue)) Fail (previsto, effettivo);
Mark Conway

3
C'è qualche motivo per usare DeepCompareinvece di chiamare semplicemente CompareExricorsivamente?
apostolov

3
Questo può confrontare l'intera struttura inutilmente. Sostituirlo resultcon return falselo renderebbe più efficiente.
Tim Sylvester

24

Se non vuoi implementare IEquatable, puoi sempre usare Reflection per confrontare tutte le proprietà: - se sono di tipo valore, confrontali semplicemente -se sono di tipo riferimento, chiama la funzione ricorsivamente per confrontare le sue proprietà "interne" .

Non penso alla performace, ma alla semplicità. Dipende però dal design esatto dei tuoi oggetti. Potrebbe complicarsi a seconda della forma degli oggetti (ad esempio se ci sono dipendenze cicliche tra le proprietà). Esistono, tuttavia, diverse soluzioni che puoi utilizzare, come questa:

Un'altra opzione è serializzare l'oggetto come testo, ad esempio utilizzando JSON.NET e confrontando il risultato della serializzazione. (JSON.NET può gestire le dipendenze cicliche tra le proprietà).

Non so se per più veloce intendi il modo più veloce per implementarlo o un codice che gira velocemente. Non dovresti ottimizzare prima di sapere se è necessario. L'ottimizzazione prematura è la radice di tutti i mali


1
Non credo che IEquatable<T>un'implementazione si qualifichi come un caso di ottimizzazione prematura. La riflessione sarà drasticamente più lenta. L'implementazione predefinita di Equalsper i tipi di valore personalizzati utilizza la riflessione; Microsoft stessa consiglia di sovrascriverlo per le prestazioni: "Sostituisci il Equalsmetodo per un tipo particolare per migliorare le prestazioni del metodo e rappresentare più da vicino il concetto di uguaglianza per il tipo".
Douglas

1
Dipende da quante volte eseguirà il metodo uguale: 1, 10, 100, 100, un milione? Ciò farà una grande differenza. Se può utilizzare una soluzione generica senza implementare nulla risparmierà del tempo prezioso. Se è troppo lento, allora è il momento di implementare IEquatable (e forse anche provare a creare un GetHashCode memorizzabile nella cache o intelligente) Per quanto riguarda la velocità di Reflection, devo ammettere che è più lento ... o molto più lento, a seconda di come lo fai ( cioè riutilizzare i tipi di PropertyInfos e così via, oppure no).
JotaBe

@ Worthy7 Sì. Per favore, guarda i contenuti del progetto. I test sono un buon modo per documentare con l'esempio. Ma, meglio di così, se lo cerchi, troverai un file della guida .chm. Quindi questo progetto ha una documentazione molto migliore rispetto alla maggior parte dei progetti.
JotaBe

Scusa hai ragione, mi mancava totalmente la scheda "wiki". Sono abituato a che tutti scrivano cose nel file readme.
Worthy7

9

Serializza entrambi gli oggetti e confronta le stringhe risultanti con @JoelFan

Quindi, per fare ciò, crea una classe statica in questo modo e usa le estensioni per estendere TUTTI gli oggetti (così puoi passare qualsiasi tipo di oggetto, raccolta, ecc. Nel metodo)

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

Dopo aver fatto riferimento a questa classe statica in qualsiasi altro file, puoi farlo:

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

Ora puoi semplicemente usare .Equals per confrontarli. Lo uso per verificare se anche gli oggetti sono nelle raccolte. Funziona davvero bene.


E se i contenuti degli oggetti fossero matrici di numeri in virgola mobile. È molto inefficiente convertirli in stringhe e la conversione è soggetta alle trasformazioni definite in CultrureInfo. Questo funzionerebbe solo se i dati interni sono principalmente stringhe e numeri interi. Altrimenti sarebbe un disastro.
John Alexiou

3
E se un nuovo director ti dicesse di estrarre C # e sostituirlo con Python. Come sviluppatori, dobbiamo imparare che le domande su cosa fare se devono finire da qualche parte. Risolvi il problema, al prossimo. Se MAI hai tempo, torna ad esso ...
ozzy432836

2
Python è più simile a MATLAB nella sintassi e nell'utilizzo. Ci deve essere davvero una buona ragione per passare da un linguaggio sicuro di tipo statico a uno script mishmash come Python.
John Alexiou,

5

Presumo che tu non ti stia riferendo letteralmente agli stessi oggetti

Object1 == Object2

Potresti pensare di fare un confronto della memoria tra i due

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

Ma non è nemmeno il codice reale in c # :). Poiché probabilmente tutti i dati vengono creati sull'heap, la memoria non è contigua e non è possibile confrontare l'uguaglianza di due oggetti in modo agnostico. Dovrai confrontare ogni valore, uno alla volta, in modo personalizzato.

Considera l'idea di aggiungere l' IEquatable<T>interfaccia alla tua classe e definire un Equalsmetodo personalizzato per il tuo tipo. Quindi, in quel metodo, testare manualmente ogni valore. Aggiungi di IEquatable<T>nuovo i tipi racchiusi se puoi e ripeti il ​​processo.

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


3

Ho trovato questa funzione di seguito per confrontare gli oggetti.

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

Lo sto usando e funziona bene per me.


1
Il primo ifsignifica ciò Compare(null, null) == falseche non è quello che mi aspetterei.
Tim Sylvester

3

Sulla base di alcune risposte già fornite qui, ho deciso di sostenere principalmente la risposta di JoelFan . Adoro i metodi di estensione e questi hanno funzionato alla grande per me quando nessuna delle altre soluzioni li avrebbe utilizzati per confrontare le mie classi complesse.

Metodi di estensione

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

}

Esempi di utilizzo

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

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

2

Direi che:

Object1.Equals(Object2)

sarebbe quello che stai cercando. Questo se stai cercando di vedere se gli oggetti sono gli stessi, che è quello che sembra che tu stia chiedendo.

Se vuoi controllare se tutti gli oggetti figli sono uguali, eseguili attraverso un ciclo con il Equals()metodo.


2
Se e solo se forniscono un sovraccarico di uguaglianza senza riferimento di Equals.
user7116

Ogni classe deve implementare il proprio modo di confronto. Se le classi dell'autore non hanno sostituzioni per il metodo Equals (), useranno il metodo di base della classe System.Object (), che porterà a errori nella logica.
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;
}

Utilizzando i principi della ricorsione.
matan justme

1

Un modo per farlo sarebbe ignorare Equals()ogni tipo coinvolto. Ad esempio, il tuo oggetto di primo livello sovrascriverà Equals()per chiamare il Equals()metodo di tutti e 5 gli oggetti figli. Anche questi oggetti dovrebbero sovrascrivere tutti Equals(), supponendo che siano oggetti personalizzati, e così via fino a quando l'intera gerarchia potrebbe essere confrontata semplicemente eseguendo un controllo di uguaglianza sugli oggetti di livello superiore.



1

Grazie all'esempio di Jonathan. L'ho espanso per tutti i casi (array, elenchi, dizionari, tipi primitivi).

Questo è un confronto senza serializzazione e non richiede l'implementazione di alcuna interfaccia per gli oggetti confrontati.

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

Per una facile copia del repository creato dal codice


1

Ora puoi usare json.net. Basta andare su Nuget e installarlo.

E puoi fare qualcosa del genere:

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

        return myself == other;
    }

Potresti forse creare un metodo di estensione per object se vuoi diventare più elaborato. Si prega di notare che questo confronta solo le proprietà pubbliche. E se si desidera ignorare una proprietà pubblica quando si esegue il confronto, è possibile utilizzare l'attributo [JsonIgnore].


Se hai elenchi nei tuoi oggetti e quelli hanno elenchi, provare a attraversare entrambi gli oggetti sarà un incubo. Se serializzi entrambi e poi li confronti, non dovrai affrontare quello scenario.
ashlar64

Se il tuo oggetto complesso contiene un dizionario, non credo che il serializzatore .net possa serializzarlo. Il serializzatore Json può.
ashlar64
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.