Сравнение двух свойств объекта просто в c # - PullRequest
5 голосов
/ 19 января 2012

У меня есть класс с большим количеством свойств.Для полной репликации объекта достаточно мелкой копии.

Мне нужно сравнить объект, просто чтобы проверить, содержит ли он точно такие же значения, как и другой.

Мои идеи:

Первое и наиболее очевидное решение - просто создать огромный блок методов, который сравнивает каждое свойство, одно за другим.

Вторым было бы сериализовать каждый объект и хешировать файл или выполнить какое-то md5контрольная сумма на нем.(Будет ли это на самом деле работать).

Третий - сделать какое-то отражение на объекте, что автоматизирует первый вариант, но создаст дополнительный уровень сложности.

Скорость не совсемпроблема.

Заинтересованы в том, чтобы услышать мысли или любые другие методы, которые мне не хватает, чтобы сделать такую ​​вещь.

Редактировать: Спасибо всем.Мое решение: (изменено, чтобы теперь быть рекурсивным для общих предметов).

  public static bool IsSame<T>(T objA, T objB)
        {
            var type = typeof(T);
            var properties = type.GetProperties();
            foreach (var pi in properties.Where(a => a.CanRead))
            {
                if (pi.Name == "Item")
                {
                    var piCount = properties.FirstOrDefault(a => a.Name == "Count");
                    int count = -1;
                    if (piCount != null && piCount.PropertyType == typeof(System.Int32))
                        count = (int)piCount.GetValue(objA, null);
                    if (count > 0)
                    {
                        for (int i = 0; i < count; i++)
                        {
                            dynamic child1 = pi.GetValue(objA, new object[] { i });
                            dynamic child2 = pi.GetValue(objB, new object[] { i });
                            return IsSame(child1, child2);
                        }
                    }
                }
                else
                {
                    var val1 = type.GetProperty(pi.Name).GetValue(objA, null);
                    var val2 = type.GetProperty(pi.Name).GetValue(objB, null);
                    if (val1 != val2 && (val1 == null || !val1.Equals(val2)))
                        return false;
                }
            }
            return true;
        }

Ответы [ 4 ]

6 голосов
/ 19 января 2012

Третий вариант (отражение) будет самым медленным, но он также будет самым надежным / способным к тестированию.

Хэш-код будет очень похож на первый вариант, так как вам придется вызыватьвсе ваши переменные-члены, поэтому для 1 и 2 вам потребуется обновлять методы .Equals(obj) и .GenerateHash() каждый раз, когда вы изменяете списки переменных-членов.

вот код, с которого можно начать:*

        foreach (FieldInfo field in this.GetType().GetFields())
        {
            if (o[field.Name] == null)
            {
                if (!field.GetValue(this).Equals(o[field.Name]))
                    return false;
            }
            else
            {
                return false;
            }
        }

        return true;
6 голосов
/ 19 января 2012

Большинство сериализаторов предназначены для обеспечения сохранения целостности данных при сериализации и десериализации, а не для создания согласованного сериализованного формата.Я бы не использовал сериализацию для этой цели.

Вы можете рассмотреть возможность реализации IEquatable , чтобы каждый экземпляр мог сравнивать себя с экземплярами того же типа.Или класс делает сравнения для вас, который реализует IEqualityComparer .То, как они делают это сравнение, может быть «большим методом», который сравнивает свойства одно за другим или использует отражение.

Отражение может быть довольно быстрым и простым способом достижения вашей цели, но может вызвать проблемы в дальнейшем (например, если кто-то добавляет свойство к вашему типу, которое не должно быть включено для сравнения равенства), но, очевидно,Обратное также верно (кто-то добавляет свойство, которое должно проверяться на равенство, но сравнение на равенство не обновляется).Какой подход вы используете, как правило, определяется тем, насколько комфортно команде каждый подход в сочетании с контекстом, в котором будет использоваться класс.

В вашем случае я бы, вероятно, рекомендовал использовать подход, основанный на отражении, поскольку вы хотите проверить результат операции мелкого клонирования, поэтому все свойства должны быть равны.

Кроме того, я бы рекомендовал использовать метод MemberwiseClone для создания клона, что уменьшило бы необходимость в таких строгих тестах.

1 голос
/ 19 января 2012

Если вы сериализуете, вы должны указать порядок сериализации для каждого поля / свойства, чтобы гарантировать, что все данные сериализуются в одном и том же порядке.Но все, чего вы добились бы - это реализовать GetHashCode () во внешнем классе.

GetHashCode () имеет несколько примеров того, как его переопределить, поэтому сначала посмотрите здесь.* Если в вашем классе много полей, и вы не хотите писать весь код вручную.Вы можете использовать рефлексию для автоматической генерации кода.Если ваш класс меняется время от времени, вы можете создать частичный класс с помощью реализации GetHashCode () в шаблоне T4.

1 голос
/ 19 января 2012

Другая мысль состоит в том, что если свойства возвращают простые типы значений, вы можете сгруппировать их в собственные типы неизменяемых значений. Например, если у вас есть клиент со свойствами string Address1 string Address2 string City string State string Zip, вы можете создать тип значения Address, который реализует свой собственный компаратор равенства, и использовать его.

Не уверен, применимо ли это к вашей ситуации, но я обнаружил, что когда у меня есть класс с большим количеством свойств, это часто можно сделать, и это делает мои классы проще и легче рассуждать о .

...