Как проверить, равны ли два объекта по своим свойствам, не нарушая существующие 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!)
Ответ 3
Рекомендуется перетащить GetHashCode() при перегрузке GetEquals().
http://msdn.microsoft.com/en-us/library/ms173147%28v=vs.80%29.aspx
например.
public override int GetHashCode()
{
return this.Username.GetHashCode() * this.Id;
}
Ответ 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"})