两个元素相同的列表为什么不相等?

5

我有以下内容:

  var a = new List<OrderRule> {
    new OrderRule("name", OrderDirection.Ascending),
    new OrderRule("age", OrderDirection.Descending)
  };


  var b = new List<OrderRule> {
    new OrderRule("name", OrderDirection.Ascending),
    new OrderRule("age", OrderDirection.Descending)
  };

  var r = a.Equals(b);

即使这两个列表中包含相等的项,变量r也是假的。OrderRule类实现了IEquality接口。请注意,当方向(Direction)和属性(Property)都相等时,两个OrderRules是相等的。

public enum OrderDirection { ASC, DESC }

public class OrderRule : IEquatable<OrderRule> {

  public OrderDirection Direction { get; }
  public String Property { get; }

  public OrderRule(String property, OrderDirection direction) {
    Direction = direction;
    Property = property;
  }

  public Boolean Equals(OrderRule other) {

    if (other == null)
      return false;

    return Property.Equals(other.Property) && Direction.Equals(other.Direction);

  }

  public override Boolean Equals(Object obj) {

    if (ReferenceEquals(null, obj))
      return false;

    if (ReferenceEquals(this, obj))
      return true;

    if (obj.GetType() != GetType())
      return false;

    return Equals(obj as IncludeRule);

  }

  public override Int32 GetHashCode() {

    return HashCode.Of(Property).And(Direction);

  }

}

我错过了什么?


8
List<T>没有覆盖重写Equals方法,基本上就是这么简单。 - Jon Skeet
1
List<T> 没有实现 IEquality,也没有重写 EqualsGetHashCode 方法,因此进行的是引用比较。您可能想尝试使用 Enumerable.SequenceEqual - juharr
我正在使用XUnit,当使用Assert.Equal(orderRule1, orderRule2)时它可以正常工作,但是当使用Assert.Equal(orderRuleList1, orderRuleList2)时就不行了...我想我需要实现一个比较器?Assert.Equal接受一个比较器...我只是试图避免这样做,因为我需要复制已经在Equals中的代码。还有其他选项吗? - Miguel Moura
1
你应该阅读这个问题:https://dev59.com/MnRC5IYBdhLWcg3wD87r - juharr
1
@MiguelMoura 可能是版本问题吗?我从未使用过XUnit,我总是需要使用类似于“Enumerable.SequenceEqual”或“CollectionAssert.AreEqual”的东西。 - juharr
显示剩余2条评论
4个回答

9

检查两个列表是否相等并不意味着检查它们是否都包含相同的项目。

如果你这样做:

var a = new List<OrderRule>();
var b = new List<OrderRule>();
var r = a.Equals(b); //false

r将始终为false,即使两个列表中的项目相同。这是因为您没有检查列表的内容。实际上,您正在检查ab是否引用同一个对象。但是a是一个对象,b是另一个对象。

如果您这样做,r将为true:

var a = new List<OrderRule>();
var b = a;
var r = a.Equals(b) //true

如果你有两个列表,并且想要查看它们是否按照相同的顺序包含相同的元素,可以这样做:

var r = a.SequenceEquals(b);

在你的问题示例中,r 将会是 true,因为你正在覆盖 OrderRule 上的 Equals 方法,所以一个列表中的项被认为与另一个列表中的项“相等”。
明白了 - 你在 OrderRule 的相等性检查有问题。
public override Boolean Equals(Object obj) {
if (ReferenceEquals(null, obj))
  return false;

if (ReferenceEquals(this, obj))
  return true;

if (obj.GetType() != GetType())
  return false;

return Equals(obj as IncludeRule);
}

我认为你想要使用return Equals(obj as OrderRule)代替return Equals(obj as IncludeRule)

关于 "var r = a.SequenceEqual(b);",你是正确的...现在它是真的了。但是为什么在XUnit中Assert.Equal(a, b)返回false呢?Assert.Equal支持集合比较。 - Miguel Moura
我从未使用过它,但我查看了文档,那就是它所说的。我假设OrderRule的相等性检查是有效的 - 但如果不是呢?也许你可以编写一个测试来检查两个相同实例的相等性。 - Scott Hannen

4

1

首先,每当使用new关键字创建新对象时,对象的引用或地址将存储在变量中。 同时,equals函数比较变量的值。

现在,您已经重写了OrderRule的equals函数。因此,如果您对两个OrderRule进行equals比较,则会得到在重写的equals函数内部进行比较的结果。

但是现在想一想,List<OrderRule>是什么?它只是一个泛型类。所以再次进行equals比较时,它将检查包含引用的变量的值,由于它们不同,因此在比较时将不会返回true。而且List实现的equals与对象相同,没有被重写。因此,我更喜欢的一种方法是创建扩展。

public static class Extensions
{
    public static bool Equal<T>(this List<T> x, List<T> y)
    {
        bool isEqual = true;
        if (x == null ^ y == null)
        {
            isEqual = false;
        }
        else if (x == null && y == null)
        {
            isEqual = true;
        }
        else if (x.Equals(y))
        {
            isEqual = true;
        }
        else if (x.Count != y.Count)
        {
            isEqual = false;
        }
        else
        {
            //This logic can be changed as per your need.
            //Here order of the list matters!
            //You can make one where order doesn't matter
            for (int i = 0; i < x.Count; i++)
            {
                if (!x[i].Equals(y[i]))
                {
                    break;
                }
            }

        }
        return isEqual;
    }
}

你不需要编写扩展来完成这个任务。Enumerable.SequenceEqual是.NET内置的扩展方法,已经实现了完全相同的逻辑。 - Scott Chamberlain
是的,没错。我完全忘记了 SequenceEqual。但这种方法可以更好地控制并且可以放置自己的逻辑,有时数据序列并不重要。但是,非常感谢你提醒我! - Sukrat Kashyap
如果您不关心顺序,请查看我的这个旧答案,我在其中将CollectionAssert.AreEquivalent转换为返回布尔值而不是抛出异常的形式。 - Scott Chamberlain

0
这样做你正在比较引用而不是对象。因此,你得到的是不等式。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接