【问题标题】:How do I check if two Objects are equal in terms of their properties only without breaking the existing Object.Equals()?如何在不破坏现有 Object.Equals() 的情况下检查两个对象的属性是否相等?
【发布时间】:2011-06-20 14:51:58
【问题描述】:

基本上,GethashCode 是不同的,即使它们包含相同的属性值...那么为什么默认返回差异哈希码?

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

【问题讨论】:

  • 应用程序运行时哈希码发生变化是正常的。 正常的是a.GetHashCode() != b.GetHashCode() -- 编写该类的人使它有问题,除非该类具有自动初始化为非固定值的重要成员(例如DateTime.Now)。
  • 另外,这里的问题是什么? (a) 如何使Equals 返回true,或者(b) 与GetHashCode 相关的东西?

标签: c# object equals hashcode


【解决方案1】:

回答为时已晚,但有人可能会在这里结束,我需要知道我的想法是对还是错。 如果严格考虑值,那么为什么不将对象设为 JSON 并比较 JSON 字符串呢?喜欢:

if (JsonConvert.SerializeObject(obj1) == JsonConvert.SerializeObject(obj2)) continue;

【讨论】:

  • 最佳答案,因为它允许代码具有动态对象。
  • 如此简单但如此聪明
【解决方案2】:

如果您安装了 ReSharper(这是值得的!)那么您所要做的就是:

Alt+Insert

在班级内使用光标。部分类有助于隐藏样板。

它会自动对每个属性进行相等检查。

(使用 Ctrl+A 选择所有属性,您可以使用空格检查所有属性!)

【讨论】:

    【解决方案3】:

    您是否尝试过实现自己的 IEqualityComparer?您可以将其传递给 .Equals() 重载来定义您自己的自定义相等逻辑,如

    用户 A = 用户 B,即使它们是不同的实例,如果属性 x、y、z 相同。

    看到这个: MSDN

    编辑:我应该写你可以实例化你的 EqualityComparer 并将两个实例传递给它的 Equals() 方法并获得一个布尔值。基本控制台应用程序...将显示真、假、假。事情是微不足道的,显示了两个属性。

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

    【讨论】:

    • +1 这是一个有效的方法,但它确实引入了很多不必要的代码。
    • 您说得对,它引入了更多代码,但正如 ebyrob 的评论所表明的那样,最佳实践是无论如何都要覆盖 GetHashCode(),而 IEqualityComparer 只有两个方法 Equals() 和 GetHashCode() 可以覆盖。就个人而言,我更喜欢分离出这种“业务逻辑”;一个单独的比较器也适用于 IoC ......但最终覆盖 Equals() 也可以正常工作。
    • @Nix:如果需要这段代码来实现应用程序的业务逻辑,那么我会称之为最需要
    • @Jon 同意,但这不是唯一的方法,而且这种方法使用更多的代码,然后只是使用扩展。
    • 当数据集有一个明显的相等比较时,为什么要为还没有自己的默认相等比较的类添加自定义 IEqualityComparer?
    【解决方案4】:

    建议在覆盖 GetEquals() 时覆盖 GetHashCode()。

    http://msdn.microsoft.com/en-us/library/ms173147%28v=vs.80%29.aspx

    例如

    public override int GetHashCode()
    {
        return this.Username.GetHashCode() * this.Id;
    }
    

    【讨论】:

      【解决方案5】:

      这是一个不需要类中的任何自定义逻辑并使用泛型来确保两个参数(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);
      

      【讨论】:

        【解决方案6】:

        为什么不编写自己的 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);
        }
        

        【讨论】:

        • 我刚刚发布了 ReSharper 如何自动执行此操作的答案,仅供参考。 :-)
        【解决方案7】:

        如果你只是想对事物进行哈希处理,可以考虑使用扩展方法来生成哈希。

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

        【讨论】:

          【解决方案8】:

          添加方法:

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

          【讨论】:

          • -1:这是一个糟糕的主意,因为它没有考虑EqualsGetHashCode 之间的关系。此外,它仅在您可以修改类时才有效。
          • 为什么需要考虑GetHashCode()?我错过了什么?当 ab 是独立的对象实例 IIRC 时,a.GetHashCode() == b.GetHashCode() 始终为 false。
          • @FlyingStreudel:这不正确。约定是当两个对象比较相等时,它们的哈希码必须也相等。您的解决方案更改了相等比较的结果,但不能确保当它返回 true 时,哈希码也将相等。 [旁白:ab 是独立的对象实例,并没有告诉我们关于它们的哈希码的任何信息]
          • @Jon - 他使用的是数组,而不是散列,但这是真的。 [旁白:如果你不实现自己的GetHashCode(),除非它们是同一个实例,否则我认为它不能保证是相等的]
          • @FlyingStreudel:我在该代码中看不到任何数组。此外,文档 (msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx) 同意我的观点:“如果两个对象比较相等,则每个对象的 GetHashCode 方法必须返回相同的值。但是,如果两个对象不相等,则 GetHashCode 方法因为这两个对象不必返回不同的值。”
          【解决方案9】:

          字符串数组是比较的排除列表。它使用反射,但性能非常好。请检查 apache commons lang 3 库

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

          【讨论】:

            猜你喜欢
            • 2017-11-26
            • 2021-10-04
            • 1970-01-01
            • 2019-10-03
            • 1970-01-01
            • 1970-01-01
            • 2016-06-27
            • 1970-01-01
            相关资源
            最近更新 更多