Как проверить, равны ли два объекта по своим свойствам, не нарушая существующие Object.Equals()?

В принципе, GethashCode отличается, хотя они содержат ТОЧНЫЕ значения для свойств... поэтому почему по умолчанию возвращаются хэш-коды diff?

public class User
{
    public Int32 Id { get; set; }
    public String Username { get; set; }
}

User a = new User();
a.Id = 1;
a.Username = "Hello";

User b = new User();
b.Id = 1;
b.Username = "Hello";

Console.WriteLine("Hash A: {0} | Hash B: {1}", a.GetHashCode(), b.GetHashCode());
//Hash A: 37121646 | Hash B: 45592480 <-- these values change each time I rerun the app?

Есть ли более правильный способ сделать это, чтобы я не нарушал работу Object.Equals для моих объектов, но все еще могу иметь свою собственную проверку равенства на основе значений параметров?

Я спрашиваю, потому что у меня есть служба: SynchronizeUsers(), которая загружает массив пользователей. Вместо того, чтобы очищать мой кеш пользователей, я бы скорее просто обновил те, которые нужно обновить, удалить те, о которых говорит синхронизация, и добавить новые. Но я не могу просто сделать Object.Equals() на этих объектах.

Ответ 1

Вы пытались реализовать свой собственный IEqualityComparer? Вы можете передать это перегрузке .Equals(), чтобы определить свою собственную логику равенства, как в

Пользователь A = Пользователь B, даже если это разные экземпляры, если свойства x, y, z одинаковы.

Смотрите это: MSDN

Изменить: я должен был написать, что вы можете создать экземпляр своего EqualityComparer и передать два экземпляра его методу Equals() и получить bool. Основное консольное приложение... покажет true, false, false. Вещь тривиальна, имеет два свойства.

var comparer = new ThingEqualityComparer();

Console.WriteLine(comparer.Equals(new Thing() { Id = 1, Name = "1" }, new Thing() { Id = 1, Name = "1" }));
Console.WriteLine(comparer.Equals(new Thing() { Id = 1, Name = "1" }, new Thing() { Id = 2, Name = "2" }));
Console.WriteLine(comparer.Equals(new Thing() { Id = 1, Name = "1" }, null));


class ThingEqualityComparer : IEqualityComparer<Thing>
{
    public bool Equals(Thing x, Thing y)
    {
        if (x == null || y == null)
            return false;

        return (x.Id == y.Id && x.Name == y.Name);
    }

    public int GetHashCode(Thing obj)
    {
        return obj.GetHashCode();
    }
}

Ответ 2

Если у вас установлен ReSharper (это того стоит!), вам нужно всего лишь:

Alt+Insert

С помощью курсора внутри класса. Частичный класс хорош для скрытия шаблона.

Он автоматически выполнит проверку равенства для каждого свойства.

(Выберите все свойства w/Ctrl + A, и вы можете проверить все w/Space!)

Ответ 4

Почему бы не написать собственный метод Equality? т.е.

User a = new User();
a.Id = 1;
a.Username = "Hello";
User b = new User();
b.Id = 1;
b.Username = "Hello";
a.IsEqualTo(b);

где IsEqualTo определен в вашем пользовательском классе как:

Public bool IsEqualTo(user compareTo)
{
  return (UserName == compareTo.UserName && Id == compareTo.Id);
}

Ответ 5

Добавьте метод:

public class User
{
    public int UserID { get; set; }

    public bool IsUser(object obj) 
    {
        return (obj is User && ((User)obj).UserID == this.UserID);
    }
}

Ответ 6

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

public static int GenerateHash(this User myUser){
    return myUser.UserName.GetHashCode() ^ ... other properties....
}

Тогда в вашем коде вы могли бы сделать:

Console.WriteLine("Hash A: {0} | Hash B: {1}", a.GenerateHash(), b.GenerateHash());

Это оставит все неповрежденным и не сломает ничего другого. Если вы ищете способ сравнения объектов, вы можете сделать то же самое с помощью метода расширения:

public static int AreEqual(this User myUser, User someOther){
    return myUser.UserName == someOther.UserName && ...  other properties.
}

Использование будет выглядеть так:

if(a.AreEqual(b)){
    // these are equal have fun.
}

Ответ 7

Вот решение, которое не требует какой-либо пользовательской логики в классе и использует обобщенные элементы, обеспечивающие одинаковый тип обоих аргументов (obj1 и obj2) во время компиляции:

  public static class ObjectComparerUtility
  {
    public static bool ObjectsAreEqual<T>(T obj1, T obj2)
    {
      var obj1Serialized = JsonConvert.SerializeObject(obj1);
      var obj2Serialized = JsonConvert.SerializeObject(obj2);

      return obj1Serialized == obj2Serialized;
    }
  }

Использование:

  var c1 = new ConcreteType { Foo = "test1" };
  var c2 = new ConcreteType { Foo = "test1" };
  var areEqual = ObjectComparerUtility.ObjectsAreEqual(c1, c2);
  Assert.IsTrue(areEqual);

Ответ 8

Массив строк - это список исключений для сравнения. Он использует отражение, но производительность очень хорошая. Пожалуйста, проверьте библиотеку Apache Commons Lang 3

CompareToBuilder.reflectionCompare(arg0, arg1, new String[]{"UID", "uidcount"})