【问题标题】:Can we compare two complex collections field by field using IEqualityComparer interface using LINQ extension method SequenceEqual我们可以使用 IEqualityComparer 接口使用 LINQ 扩展方法 SequenceEqual 逐字段比较两个复杂集合吗
【发布时间】:2019-10-09 15:48:09
【问题描述】:

我正在尝试使用 IEqualityComparer 逐个字段比较 2 个集合中的 2 个字段。 IEqualityComparer 仅比较 1 个字段“名称”。我也想比较“mark”。

在Java中,我们有comparator接口来比较Equals方法中的多个字段。

using System;
using System.Linq;
using System.Collections.Generic;

    public class Program
    {
        public static void Main()
        {
            IList<Student> studentList1 = new List<Student>()
            {
                new Student(){ name="aaaaa", mark = 95, },
                new Student(){ name="bbbb", mark = 25, },
                new Student(){ name="ccc",  mark = 80 }
            };

            IList<Student> studentList2 = new List<Student>()
            {
                new Student(){ name="aaaaa", mark = 95, },
                new Student(){ name="bbbb", mark = 5, },
                new Student(){ name="ccc",  mark = 80 }
            };

            bool isEqual = studentList1.SequenceEqual(studentList2, new StudentComparer());
            Console.WriteLine("Names in 2 collections are {0}", isEqual?"equal":"not equal");   
        }
    }

    public class Student
    {
        public string name { get; set; }
        public int mark { get; set; }
    }

    public class StudentComparer : IEqualityComparer<Student>
    {
        public bool Equals(Student x, Student y)
        {
            if (x.name == y.name)
                return true;
            return false;
        }

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

实际结果: 2 个集合中的名称相等 预期结果: 2 个集合中的名称相等 2个集合中的分数不相等

【问题讨论】:

  • 使用反射,比较这两个对象的每个属性,就知道每个属性的值是否相等..
  • 为什么不在 equals 方法中添加标记比较呢?你怎么能期望一个控制台写行有 2 个输出?
  • Console.WriteLine("2 个集合中的标记为 {0}", isEqual?"equal":"not equal");我将添加这一行。此外,我还将添加标记比较。但我不确定如何在 Equals 方法中编写标记比较。 linq 查询如何区分名称和标记比较之间的差异。
  • if (x.name == y.name && x.mark == y.mark) 返回真; //这样我也可以实现2个字段的比较。有没有其他方法可以区分 linq 查询。 bool isNameEqual = studentList1.SequenceEqual(studentList2, new StudentComparer()); //使用这个我将打印名称是相等的。 bool isMarkEqual = studentList1.SequenceEqual(studentList2, new StudentComparer()); //使用这个我将打印标记不相等。

标签: c# linq iequalitycomparer


【解决方案1】:

您的问题是 Object.GetHashCode() 在对象之间必然是唯一的。这意味着当SequenceEqual(…) 调用comparer.GetHashCode(…) 时,尽管您在StudentComparer.Equals(…) 中声明它们相等,但它会为不同的对象实例获得不同的值。

所有这一切的意思是,您应该返回唯一的哈希码,仅在根据 IEqualityComparer 的 Student 实例是唯一的情况下。例如,您可以将 StudentComparer.GetHashCode(Student obj) 的实现更改为: return obj.name.GetHashCode()

【讨论】:

    【解决方案2】:

    需要像这样正确实现相等比较器(此代码已由R#生成):

    public sealed class StudentComparer  : IEqualityComparer<Student>
    {
        public bool Equals(Student x, Student y)
        {
            if (ReferenceEquals(x, y)) return true;
            if (ReferenceEquals(x, null)) return false;
            if (ReferenceEquals(y, null)) return false;
            if (x.GetType() != y.GetType()) return false;
            return string.Equals(x.name, y.name) && x.mark == y.mark;
        }
    
        public int GetHashCode(Student obj)
        {
            unchecked
            {
                return ((obj.name != null ? obj.name.GetHashCode() : 0) * 397) ^ obj.mark;
            }
        }
    }
    

    PSWhy is '397' used for ReSharper GetHashCode override?.

    【讨论】:

      【解决方案3】:

      基本上您需要检查Equals 方法中的名称和标记是否相等,并分别获取name 属性的哈希码以及mark 属性以使IEqualityComparer 正常工作。

      将您的代码修改为:

      public class Program
      {
          public static void Main()
          {
              IList<Student> studentList1 = new List<Student>()
              {
                  new Student { name = "aaaaa", mark = 95, },
                  new Student { name = "bbbb", mark = 25, },
                  new Student { name = "ccc",  mark = 80 }
              };
      
              IList<Student> studentList2 = new List<Student>()
              {
                  new Student { name = "aaaaa", mark = 95, },
                  new Student { name = "bbbb", mark = 5, },
                  new Student { name = "ccc",  mark = 80 }
              };
      
              bool isEqual = studentList1.SequenceEqual(studentList2, new StudentComparer());
              Console.WriteLine("Contents in 2 collections are {0}", isEqual ? "equal" : "not equal");
          }
      }
      
      public class Student
      {
          public string name { get; set; }
          public int mark { get; set; }
      }
      
      public class StudentComparer : IEqualityComparer<Student>
      {
          public bool Equals(Student x, Student y)
          {
              //Check whether the compared objects reference the same data. 
              if (object.ReferenceEquals(x, y))
                  return true;
      
              //Check whether any of the compared objects is null. 
              if (object.ReferenceEquals(x, null) || object.ReferenceEquals(y, null))
                  return false;
      
              return string.Equals(x.name, y.name, StringComparison.OrdinalIgnoreCase) && x.mark == y.mark;
          }
      
          public int GetHashCode(Student student)
          {
              //Check whether the object is null 
              if (object.ReferenceEquals(student, null))
                  return 0;
      
              //Get hash code for the name field if it is not null
              int nameHashCode = !string.IsNullOrEmpty(student.name) ? 0 : student.name.GetHashCode();
      
              // Get hash code for marks also if its not 0
              int marksHashCode = student.mark == 0 ? 0 : student.mark.GetHashCode();
      
              return nameHashCode ^ marksHashCode;
          }
      }
      

      输出:

       Contents in 2 collections are not equal
      

      【讨论】:

      • if (x.name == y.name && x.mark == y.mark) 返回真; //这样我也可以实现2个字段的比较。有没有其他方法可以区分 linq 查询。 bool isNameEqual = studentList1.SequenceEqual(studentList2, new StudentComparer()); //使用这个我将打印名称是相等的。 bool isMarkEqual = studentList1.SequenceEqual(studentList2, new StudentComparer()); //使用这个我将打印标记不相等。
      • @HEMAMBUJAVALLYVIRAPPANE 基本上你不能使用SequenceEqual 来检查你所期望的个别属性
      【解决方案4】:

      我建议您将 IEquatable 接口添加到您的 Student 类,因为它的代码更少,并且更清楚您想要实现的目标:

      public class Student : IEquatable<Student>
      {
          public string name { get; set; }
          public int mark { get; set; }
      
      public bool Equals(Student other)
          {
              if (string.Equals(name, other.name) && mark == other.mark)
              {
                  return true;
              }
      
              return false;
          }
      }
      

      您可以省略额外的 StudentComparer 实现。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-12-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-05-05
        • 1970-01-01
        相关资源
        最近更新 更多