【问题标题】:Union-ing two custom classes returns duplicates合并两个自定义类返回重复项
【发布时间】:2010-06-28 16:00:55
【问题描述】:

我有两个自定义类,ChangeRequestChangeRequests,其中一个 ChangeRequests 可以包含许多 ChangeRequest 实例。

public class ChangeRequests : IXmlSerializable, ICloneable, IEnumerable<ChangeRequest>,
    IEquatable<ChangeRequests> { ... }

public class ChangeRequest : ICloneable, IXmlSerializable, IEquatable<ChangeRequest>
    { ... }

我正在尝试合并两个 ChangeRequests 实例。但是,似乎没有删除重复项。我的 MSTest 单元测试如下:

var cr1 = new ChangeRequest { CRID = "12" };
var crs1 = new ChangeRequests { cr1 };
var crs2 = new ChangeRequests
               {
                   cr1.Clone(),
                   new ChangeRequest { CRID = "34" }
               };
Assert.AreEqual(crs1[0], crs2[0], "First CR in both ChangeRequests should be equal");
var unionedCRs = new ChangeRequests(crs1.Union<ChangeRequest>(crs2));
ChangeRequests expected = crs2.Clone();
Assert.AreEqual(expected, unionedCRs, "Duplicates should be removed from a Union");

测试在最后一行失败,unionedCRs 包含两个cr1 的副本。当我尝试调试并单步执行每一行时,我在第一行的ChangeRequest.Equals(object)ChangeRequest.Equals(ChangeRequest) 的第一行都有一个断点,但都没有命中。为什么联合包含重复的ChangeRequest 实例?

编辑:按要求,这里是ChangeRequests.Equals(ChangeRequests)

public bool Equals(ChangeRequests other)
{
    if (ReferenceEquals(this, other))
    {
        return true;
    }

    return null != other && this.SequenceEqual<ChangeRequest>(other);
}

这里是ChangeRequests.Equals(object)

public override bool Equals(object obj)
{
    return Equals(obj as ChangeRequests);
}

编辑: 我在ChangeRequestChangeRequests 上都覆盖了GetHashCode,但仍然在我的测试中,如果我这样做IEnumerable&lt;ChangeRequest&gt; unionedCRsIEnum = crs1.Union&lt;ChangeRequest&gt;(crs2);unionedCRsIEnum 最终会得到@ 的两个副本987654342@ 与CRID 12。

编辑: 我的EqualsGetHashCode 实现必须在某处出现问题,因为Assert.AreEqual(expected, unionedCRs.Distinct(), "Distinct should remove duplicates"); 失败,而expectedunionedCRs.Distinct() 的字符串表示表明@ 987654349@ 肯定有两份 CR 12。

【问题讨论】:

  • 你能发布你对ChangeRequests.EqualsChangeRequests.GetHashCode的实现吗?很容易在其中一个中打错字并破坏对象身份。
  • @Tim:我在我的问题中添加了ChangeRequests.Equals 的两个实现,但我没有覆盖GetHashCode...也许我应该这样做!
  • 对,您的 GetHashCode 需要与您的 Equals 保持一致 - Union 方法似乎同时使用两者。
  • 您绝对应该重写 GetHashCode。我很惊讶编译器还没有警告过你。
  • @Tim:你应该发布你的“修复GetHashCode”建议作为答案——我会选择它作为选择的!原来这是一个GetHashCode 问题。 Union 现在按预期工作。 :)

标签: c# linq set union equals


【解决方案1】:

确保您的 GetHashCode 实现与您的 Equals 一致 - Enumerable.Union 方法似乎同时使用两者。

如果您实现了一个但没有实现另一个,您应该从编译器收到警告;您仍然需要确保这两种方法彼此一致。这是规则的方便摘要:Why is it important to override GetHashCode when Equals method is overridden?

【讨论】:

    【解决方案2】:

    我不相信Assert.AreEqual() 会检查序列的内容 - 它会比较序列对象本身,它们显然不相等。

    你想要的是一个SequenceEqual() 方法,它实际上会检查两个序列的内容。 This answer may help you。这是对类似问题的回答,描述了如何与IEnumerable&lt;&gt; 序列进行比较。

    您可以轻松地获取响应者的答案,并创建一个扩展方法以使调用看起来更像断言:

    public static class AssertionExt
    {
      public static bool AreSequencesEqual<T>( IEnumerable<T> expected, 
                                               IEnumerable<T> sequence )
      {
        Assert.AreEqual(expected.Count(), sequence .Count()); 
    
        IEnumerator<Token> e1 = expected.GetEnumerator(); 
        IEnumerator<Token> e2 = sequence .GetEnumerator(); 
    
        while (e1.MoveNext() && e2.MoveNext()) 
        { 
            Assert.AreEqual(e1.Current, e2.Current); 
        }
      }
    }
    

    或者,您可以使用SequenceEqual() 来比较序列,意识到它不会提供有关哪些元素不相等的任何信息。

    【讨论】:

    • 尝试SequenceEqual 仍然失败。我已经在ChangeRequests 上覆盖了Equals,所以在两个ChangeRequests 上调用AreEqual 应该可以工作。我的ChangeRequests.Equals 覆盖比较序列。
    • @Sarah - 奇怪的是 SequenceEqual 不起作用 - 它应该使用默认的相等比较器,反过来,它应该测试 T 是否是 IEquatable&lt;T&gt;,并使用适合的相等比较器那个实现。
    【解决方案3】:

    正如 LBushkin 所说,Assert.AreEqual 只会在序列上调用 Equals

    您可以使用SequenceEqual 扩展方法:

    Assert.IsTrue(expected.SequenceEqual(unionedCRs));
    

    但是,如果失败,它不会提供太多信息。

    您可能想要使用以序列为中心的test code we wrote for MoreLINQ - 如果序列不相等,它将指定它们的不同方式。 (我正在尝试获取有问题的源文件的链接,但我的网络连接很垃圾。)

    【讨论】:

    • 我已经在ChangeRequests 上覆盖了Equals,所以我认为使用AreEqual 应该可以工作。用Assert.IsTrue(expected.SequenceEqual&lt;ChangeRequest&gt;(unionedCRs), "Duplicates should be removed from a Union"); 替换我的测试的最后一行仍然失败。
    • @Sarah:使用 Assert.Equal,它不会在 ChangeRequests 类型上调用 Equals - 它在 sequence 上调用 Equals,它不会覆盖 Equals。不过它应该与 SequenceEqual 一起使用。
    猜你喜欢
    • 1970-01-01
    • 2018-11-14
    • 1970-01-01
    • 1970-01-01
    • 2012-11-30
    • 2023-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多