【问题标题】:Two lists with same elements are not equal. Why?具有相同元素的两个列表不相等。为什么?
【发布时间】:2016-07-01 18:08:50
【问题描述】:

我有以下几点:

  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 变量也是假的。 OrdeRule 类实现了 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);

  }

}

我错过了什么?

【问题讨论】:

  • List&lt;T&gt; 不会覆盖 Equals,基本上 - 就这么简单。
  • List&lt;T&gt; 没有实现IEquality 也没有覆盖EqualsGetHashCode,所以这是一个参考比较。您可能想改用Enumerable.SequenceEqual
  • 我在 XUnit 上使用它,使用 Assert.Equal(orderRule1, orderRule2);但不是在使用 Assert.Equal(orderRuleList1, orderRuleList2); ...我想我需要实现一个比较器? Assert.Equal 接受一个比较器......我只是想避免这种情况,因为我需要复制我在 Equals 中已有的代码。还有其他选择吗?
  • 你应该阅读这个问题然后stackoverflow.com/questions/419659/…
  • @MiguelMoura 也许是版本问题?我从未使用过 XUnit,而且我总是需要使用类似 Enumerable.SequenceEqualCollectionAssert.AreEqual 的东西

标签: c# .net


【解决方案1】:

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

如果你这样做:

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

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

如果你这样做了,r 将是真的:

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 IncludeRule),而是return Equals(obj as OrderRule)

【讨论】:

  • 你是对的“var r = a.SequenceEqual(b);” ......现在是真的。但是为什么 Assert.Equal(a, b) 在 XUnit 中返回 false。 Assert.Equal 支持集合比较。
  • 我从未使用过它,但我查看了文档,这就是它所说的。我假设OrderRule 的平等检查正在工作 - 如果不是怎么办?也许您可以编写一个测试来检查两个相同的实例是否相等。
【解决方案2】:

您的代码正在测试它们是否是同一个列表。如果它们的内容相同,则不会。

a.Equals(b); // false
var d = a;
d.Equals(a); // true

如果要比较内容,请使用 linq 的 SequenceEqual

https://msdn.microsoft.com/en-us/library/bb348567(v=vs.100).aspx

【讨论】:

    【解决方案3】:

    首先,每当使用new 关键字或创建新对象时,对象的引用或地址都存储在变量中。 也等于比较变量的值。

    现在您已经覆盖了OrderRule 的equals 函数。因此,如果您对两个 OrderRule 执行 equals 操作,您将在覆盖的 equals 函数中进行比较时得到结果。

    但现在想想List&lt;OrderRule&gt; 是什么,它不过是一个泛型类。因此,equals 将再次检查包含引用的变量的值,并且由于它们不同,因此在比较它们时不会得到正确的结果。 List 也实现了与未被覆盖的对象相同的等号。因此,我更喜欢的一种方法是创建扩展。

    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 的扩展方法,它已经完成了完全相同的逻辑。
    • 是的,我完全忘记了SequenceEqual。但是这样一来,人们就有了更多的控制权,并且可以设置自己的逻辑,因为有时数据的顺序并不重要。但是,是的,谢谢你提醒我!
    • 如果您不关心订单,请查看this old answer of mine,我将CollectionAssert.AreEquivalent 转换为返回布尔值而不是抛出异常的东西。
    【解决方案4】:

    这样做是在比较引用而不是对象。所以你会返回不等式。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-12-27
      • 2018-11-27
      • 2023-03-17
      • 1970-01-01
      • 2023-03-12
      • 1970-01-01
      • 2019-08-18
      • 1970-01-01
      相关资源
      最近更新 更多