Найкращий спосіб порівняння двох складних об'єктів


112

У мене є два складні об’єкти, як Object1і Object2. У них близько 5 рівнів дочірніх об'єктів.

Мені потрібен найшвидший метод сказати, чи вони однакові чи ні.

Як це можна зробити в C # 4.0?

Відповіді:


101

Реалізуйте IEquatable<T>(як правило, у поєднанні з переосмисленням успадкованих Object.Equalsта Object.GetHashCodeметодів) для всіх власних типів. Що стосується складених типів, посилайтесь на Equalsметод містяться типів у межах містять типів. Для колекцій, що містяться, використовуйте SequenceEqualметод розширення, який внутрішньо викликає IEquatable<T>.Equalsабо Object.Equalsна кожному елементі. Цей підхід, очевидно, вимагатиме від вас розширити визначення типів, але його результати швидші, ніж будь-які загальні рішення, що включають серіалізацію.

Редагувати : Ось надуманий приклад з трьома рівнями гніздування.

Для типів значень зазвичай можна просто викликати їх Equalsметод. Навіть якщо поля чи властивості ніколи не були призначені явно, вони все одно матимуть значення за замовчуванням.

Для посилальних типів слід спочатку зателефонувати ReferenceEquals, який перевіряє рівність еталону - це послужило б підвищенню ефективності, коли ви посилаєтесь на один і той же об’єкт. Він також оброблятиме випадки, коли обидві посилання є недійсними. Якщо ця перевірка не вдалася, переконайтеся, що поле чи властивість вашого примірника не є нульовими (щоб уникнути NullReferenceException) та зателефонуйте на його Equalsметод. Оскільки наші члени правильно набрані, IEquatable<T>.Equalsметод отримує виклик безпосередньо, минаючи переохоплений Object.Equalsметод (виконання якого буде незначно повільнішим через передачу типу).

Коли ви перекриваєте Object.Equals, від вас також очікується перевибір Object.GetHashCode; Я не робив цього нижче заради лаконічності.

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

Оновлення : Ця відповідь була написана кілька років тому. Відтоді я почав відхилятися від впровадження IEquality<T>змінних типів для таких сценаріїв. Існує два поняття рівності: тотожність та еквівалентність . На рівні представлення пам’яті їх в народі розрізняють як «еталонна рівність» та «ціннісна рівність» (див. Порівняння рівності ). Однак те саме відмінність може застосовуватися і на рівні домену. Припустимо, у вашому Personкласі є PersonIdвластивість, унікальна для різних реальних людей. Чи слід два об’єкти з однаковими, PersonIdале різними Ageзначеннями вважати рівними чи різними? Відповідь вище припускає, що після еквівалентності. Тим не менш, існує багато звичаївIEquality<T>інтерфейс, такий як колекції, який передбачає, що такі реалізації забезпечують ідентичність . Наприклад, якщо ви заповнюєте HashSet<T>, ти, як правило, очікує, що TryGetValue(T,T)виклик поверне існуючі елементи, які поділяють лише ідентичність вашого аргументу, не обов'язково еквівалентні елементи, вміст яких повністю однаковий. Це поняття виконується примітками про GetHashCode:

Загалом, для змінних посилальних типів слід переосмислювати GetHashCode()лише якщо:

  • Ви можете обчислити хеш-код із полів, які не змінюються; або
  • Ви можете переконатися, що хеш-код змінного об'єкта не змінюється, поки об'єкт міститься у колекції, що спирається на його хеш-код.

Я отримую цей об’єкт через RIA Services ... Чи можу я використовувати IEquatable <Foo> для цих об’єктів і отримати його під клієнт WPF?
Розробник

1
Ви маєте на увазі, що класи автоматично генеруються? Я не використовував RIA Services, але я б припустив, що будь-які згенеровані класи будуть оголошені як partial- у такому випадку, так, ви можете реалізувати їх Equalsметод через додане вручну часткове оголошення класу, яке посилається на поля / властивості з автоматично створених один.
Дуглас

Що робити, якщо "Адреса адреси" насправді "Адреса [] адреси", як це буде реалізовано?
guiomie

2
Ви могли б назвати LINQ Enumerable.SequenceEqualметод на масиви: this.Addresses.SequenceEqual(other.Addresses). Це внутрішньо викликає ваш Address.Equalsметод для кожної пари відповідних адрес, враховуючи, що Addressклас реалізує IEquatable<Address>інтерфейс.
Дуглас

2
Ще одна категорія порівняння, яку може перевірити розробник, - "WorksLike". Для мене це означає, що хоча два екземпляри можуть мати деякі нерівні значення властивостей, програма дасть однаковий результат при обробці двох екземплярів.
Джон Курц

95

Серіалізуйте обидва об'єкти та порівняйте отримані рядки


1
Я не бачу, чому це було б. Як правило, серіалізація є оптимізованим процесом, і вам потрібно отримати доступ до цінності кожного ресурсу в будь-якому випадку.
JoelFan

5
Там величезна вартість. Ви генеруєте потік даних, додаєте рядки, а потім тестуєте рівність рядків. Ордени величини, саме в цьому. Не кажучи вже про серіалізацію, за замовчуванням буде використовувати відображення.
Джеронім Халтом

2
Потік даних не є великим питанням, я не бачу, чому вам потрібно буде додавати рядки ... тестування рівності рядків - одна з найбільш оптимізованих операцій там .... у вас може бути точка з відображенням ... але на вся серіалізація не буде на "порядки" гіршою, ніж інші методи. Ви повинні робити орієнтири, якщо ви підозрюєте проблеми з продуктивністю ... Я не відчував проблем із продуктивністю цього методу
JoelFan

12
Я +1це просто тому, що я ніколи не думав про те, щоб зробити порівняння на основі цінностей таким чином. Це приємно і просто. Було б акуратно побачити деякі орієнтири з цим кодом.
Томас

1
Це не гарне рішення, оскільки обидві серіалізації можуть піти не так, як подібний шлях. Наприклад, деякі властивості вихідного об'єкта, можливо, не були серіалізовані, і коли deserialized буде встановлено в нульовому для об'єкта призначення. У такому випадку ваш тест, який порівнює рядки, пройде, але обидва об'єкти насправді не однакові!
stackMeUp

35

Ви можете використовувати метод розширення, рекурсію для вирішення цієї проблеми:

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

або порівняйте за допомогою Json (якщо об'єкт дуже складний) Ви можете використовувати 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
Перше рішення чудове! Мені подобається, що вам не потрібно json серіалізувати чи реалізовувати додавання будь-якого коду до самих об’єктів. Підходить, коли ви тільки порівнюєте для одиничних тестів. Я можу запропонувати додати просте порівняння у випадку, якщо objValue та anotherValue обидва рівні нульовими? Це дозволить уникнути закидання NullReferenceException при спробі зробити null.Equals () // ReSharper відключити один раз RedundantJumpStatement, якщо (objValue == anotherValue) продовжувати; // нульовий довідковий захист else if (! objValue.Equals (anotherValue)) Fail (очікуваний, фактичний);
Марк Конвей

3
Чи є якась причина використовувати DeepCompareзамість того, щоб просто дзвонити CompareExрекурсивно?
апостолов

3
Це може непотрібно порівнювати всю структуру. Заміна resultна return falseце зробить її більш ефективною.
Тім Сильвестр

24

Якщо ви не хочете реалізувати IEquatable, ви завжди можете використовувати Reflection для порівняння всіх властивостей: - якщо вони мають значення типу, просто порівняйте їх - якщо вони є еталонним типом, викликайте функцію рекурсивно, щоб порівняти її "внутрішні" властивості .

Я думаю не про перформанс, а про простоту. Однак це залежить від точного дизайну ваших об'єктів. Це може ускладнитися залежно від форми об'єктів (наприклад, якщо між властивостями існують циклічні залежності). Однак є кілька рішень, якими ви можете скористатися, як це:

Інший варіант - серіалізація об'єкта у вигляді тексту, наприклад за допомогою JSON.NET, та порівняння результатів серіалізації. (JSON.NET може обробляти циклічні залежності між властивостями).

Я не знаю, чи найшвидше ви маєте на увазі найшвидший спосіб його виконання або код, який швидко працює. Не варто оптимізувати, перш ніж знати, чи потрібно. Передчасна оптимізація - корінь усього зла


1
Я навряд чи думаю, що IEquatable<T>реалізація кваліфікується як випадок передчасної оптимізації. Відбиття буде різко повільніше. Реалізація за замовчуванням Equalsдля спеціальних типів значень використовує відображення; Самі Microsoft рекомендують переосмислити його для продуктивності: "Перевизначте Equalsметод для певного типу, щоб поліпшити продуктивність методу та більш детально представити концепцію рівності для типу".
Дуглас

1
Це залежить від того, скільки разів він збирається запустити метод рівних: 1, 10, 100, 100, мільйон? Це матиме велике значення. Якщо він зможе використати загальне рішення, не втілюючи нічого, він шкодує дорогоцінний час. Якщо це занадто повільно, то настав час впровадити IEquatable (і, можливо, навіть намагатися зробити кешований або розумний GetHashCode) Що стосується швидкості відображення, я повинен погодитися, що вона повільніше ... або набагато повільніше, залежно від того, як ви це робите ( тобто повторне використання типів PropertyInfos тощо, чи ні).
JotaBe

@ Варто7 Це робить. Будь ласка, дивіться зміст проекту. Тести - це хороший спосіб документування на прикладі. Але краще, якщо ви шукаєте, ви знайдете файл довідки .chm. Тож цей проект має набагато кращу документацію, ніж більшість проектів.
JotaBe

Вибачте, що ви праві, я зовсім пропустив вкладку "вікі". Я звик, щоб усі писали речі в читанні.
Варто7

9

Серіалізуйте обидва об'єкти та порівняйте отримані рядки від @JoelFan

Тому для цього створіть статичний клас на кшталт цього і використовуйте розширення для розширення ВСІХ об'єктів (щоб ви могли передавати будь-який тип об'єкта, колекції тощо у метод)

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

Після посилання цього статичного класу в будь-який інший файл, ви можете зробити це:

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

Тепер ви можете просто використовувати .Equals, щоб порівняти їх. Я використовую це для перевірки наявності об'єктів у колекціях. Це працює дуже добре.


Що робити, якщо вміст об'єктів - це масиви чисел з плаваючою точкою. Перетворити їх у рядки дуже неефективно, і перетворення підлягає перетворенням, визначеним у CultrureInfo. Це працює лише в тому випадку, якщо внутрішні дані - це переважно рядки та цілі числа. Інакше це було б лихом.
Джон Алексіу

3
Що робити, якщо новий директор сказав вам зірвати C # і замінити його на Python. Як розробники, ми повинні дізнатися, що, що, якщо питання повинні десь зупинитися. Вирішіть проблему на наступному. Якщо ви коли-небудь знайдете час, поверніться до нього ...
ozzy432836

2
Python більше нагадує MATLAB у синтаксисі та використанні. Має бути справді вагома причина перейти від безпечної мови статичного типу до скрипту мішмашу, як python.
Джон Алексіу

5

Я припускаю, що ви не маєте на увазі буквально однакових об’єктів

Object1 == Object2

Ви можете задуматися про те, щоб зробити порівняння пам’яті між ними

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

Але це навіть не реальний код у c # :). Оскільки всі ваші дані, ймовірно, створені в купі, пам’ять не є суміжною, і ви не можете просто порівняти рівність двох об'єктів агностично. Вам доведеться порівнювати кожне значення, по одному, на власний спосіб.

Подумайте про додавання IEquatable<T>інтерфейсу до свого класу та визначте власний Equalsметод для вашого типу. Потім у цьому методі вручну перевіряйте кожне значення. IEquatable<T>Якщо ви можете, повторіть додавання у закритих типах.

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


3

Я знайшов цю функцію нижче для порівняння об'єктів.

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

Я використовую його, і він працює добре для мене.


1
Перший ifозначає те, Compare(null, null) == falseщо не те, що я очікував.
Тім Сильвестр

3

Виходячи з декількох відповідей, вже даних тут, я вирішив здебільшого відповісти на відповідь Джоельфана . Я люблю методи розширення, і вони чудово працювали для мене, коли жодне з інших рішень не використовувало б їх для порівняння моїх складних занять.

Методи розширення

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

}

Приклади використання

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

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

2

Я б сказав, що:

Object1.Equals(Object2)

було б те, що ви шукаєте. Це якщо ви хочете дізнатися, чи об’єкти однакові, про що ви, здається, запитуєте.

Якщо ви хочете перевірити, чи всі дочірні об’єкти однакові, запустіть їх через цикл Equals()методом.


2
Якщо і тільки якщо вони забезпечують нереференційне перевантаження рівності рівних.
user7116

Кожен клас повинен реалізувати власний спосіб порівняння. Якщо в авторських класах немає переопределення методу Equals (), вони будуть використовувати базовий метод класу System.Object (), що призведе до помилок у логіці.
Діма

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

1

Одним із способів зробити це було б переосмислення Equals()кожного залученого типу. Наприклад, ваш об'єкт верхнього рівня замінить Equals()виклик Equals()методу всіх 5 дочірніх об'єктів. Ці об'єкти повинні також переосмислюватись Equals(), якщо припустити, що вони є власними об'єктами, і так далі, поки всю ієрархію не вдасться порівняти, виконавши перевірку рівності об'єктів верхнього рівня.



1

Завдяки прикладу Джонатана. Я розширив його на всі випадки (масиви, списки, словники, примітивні типи).

Це порівняння без серіалізації та не вимагає реалізації будь-яких інтерфейсів для порівняних об'єктів.

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

Для легкої копіювання коду створено сховище


1

Тепер ви можете використовувати json.net. Просто перейдіть на Nuget і встановіть його.

І ви можете зробити щось подібне:

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

        return myself == other;
    }

Можливо, ви можете зробити метод розширення для об'єкта, якби хотіли отримати фантазії. Зверніть увагу, це порівнює лише загальнодоступні властивості І якщо ви хочете ігнорувати загальнодоступну власність під час порівняння, ви можете використовувати атрибут [JsonIgnore].


Якщо у вас є списки у ваших об'єктах, а у них є списки, то спроба об'їхати обидва об'єкти стане кошмаром. Якщо ви серіалізуєте і те, і потім порівняєте, вам не доведеться мати справу з цим сценарієм.
ашлар64

Якщо у вашому складному об'єкті є Словник, я не вірю, що серіалізатор .net може його серіалізувати. Серіалізатор Json може.
ashlar64
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.