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

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

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

我试图做两个ChangeRequests实例的联合。 但是,似乎没有删除重复项。 我的MSTestunit testing如下:

 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(crs2)); ChangeRequests expected = crs2.Clone(); Assert.AreEqual(expected, unionedCRs, "Duplicates should be removed from a Union"); 

测试在最后一行失败, unionedCRs包含两个unionedCRs副本。 当我尝试调试并逐步执行每一行时,我在第一行的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(other); } 

这里是ChangeRequests.Equals(object)

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

编辑:我在ChangeRequestChangeRequests上覆盖GetHashCode ,但仍然在我的测试中,如果我执行IEnumerable unionedCRsIEnum = crs1.Union(crs2);unionedCRsIEnum最终得到两份带有CRID 12的ChangeRequest副本。

编辑:因为Assert.AreEqual(expected, unionedCRs.Distinct(), "Distinct should remove duplicates");某些东西必须在我的EqualsGetHashCode实现的某处Assert.AreEqual(expected, unionedCRs.Distinct(), "Distinct should remove duplicates"); 如果失败了, unionedCRs.Distinct()unionedCRs.Distinct()的字符串表示显示unionedCRs.Distinct()肯定有两个CR 12副本。

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

如果你已经实现了一个而不是另一个,你应该从编译器得到警告; 你仍然需要确保两种方法相互一致。 以下是规则的简单摘要: 为什么在重写Equals方法时重写GetHashCode很重要?

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

你想要的是一个SequenceEqual()方法,它将实际检查两个序列的内容。 这个答案可能对你有帮助 。 这是对类似问题的回答,描述了如何与IEnumerable<>序列进行比较。

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

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

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

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

您可以使用SequenceEqual扩展方法:

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

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

您可能希望使用我们为MoreLINQ编写的测试代码,这些代码是以序列为中心的 – 如果序列不相等,它将以不同的方式指定它们。 (我正在尝试获取有问题的源文件的链接,但我的网络连接是垃圾。)