联合两个自定义类将返回重复项

时间:2010-06-28 16:00:55

标签: c# linq set union equals

我有两个自定义类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);
}

修改:我在GetHashCodeChangeRequest上覆盖了ChangeRequests,但仍在我的测试中,如果我IEnumerable<ChangeRequest> unionedCRsIEnum = crs1.Union<ChangeRequest>(crs2);,{{1}最终得到unionedCRsIEnum ChangeRequest 12的两份副本。

修改:我的CRIDEquals实施在某处,因为GetHashCode失败,以及{{1}的字符串表示形式。 }和Assert.AreEqual(expected, unionedCRs.Distinct(), "Distinct should remove duplicates");表明expected肯定有两份CR 12。

3 个答案:

答案 0 :(得分:4)

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

如果你已经实现了一个而不是另一个,你应该收到编译器的警告;你仍然需要确保两种方法相互一致。以下是规则的简单摘要:Why is it important to override GetHashCode when Equals method is overridden?

答案 1 :(得分:3)

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

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

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

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()来比较序列,意识到它不会提供有关哪些元素不相等的任何信息。

答案 2 :(得分:0)

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

您可以使用SequenceEqual扩展名方法:

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

然而,如果它失败,那将不会提供太多信息。

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