Объединение двух пользовательских классов возвращает дубликаты - PullRequest
3 голосов
/ 28 июня 2010

У меня есть два пользовательских класса, ChangeRequest и ChangeRequests, где 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);
}

Редактировать: Я преодолел GetHashCode на ChangeRequest и ChangeRequests, но все еще в моем тесте, если я выполню IEnumerable<ChangeRequest> unionedCRsIEnum = crs1.Union<ChangeRequest>(crs2);, unionedCRsIEnum заканчивается двумякопии ChangeRequest с CRID 12.

Edit: что-то должно быть в моих реализациях Equals или GetHashCode где-то, так как Assert.AreEqual(expected, unionedCRs.Distinct(), "Distinct should remove duplicates"); терпит неудачу, истроковые представления expected и unionedCRs.Distinct() показывают, что unionedCRs.Distinct() определенно имеет две копии CR 12.

Ответы [ 3 ]

4 голосов
/ 29 июня 2010

Убедитесь, что ваша реализация GetHashCode соответствует вашей Equals - метод Enumerable.Union, кажется, использует оба.

Вы должны получить предупреждение от компилятора, если вы внедрили одно, но не другое; Вы должны убедиться, что оба метода согласуются друг с другом. Вот удобная сводка правил: Почему важно переопределить GetHashCode, если переопределен метод Equals?

3 голосов
/ 28 июня 2010

Я не верю, что Assert.AreEqual() проверяет содержимое последовательности - она ​​сравнивает сами объекты последовательности, которые явно не равны.

То, что вы хотите, это SequenceEqual() метод, который фактически проверяет содержимое двух последовательностей. Этот ответ может помочь вам .Это ответ на аналогичный вопрос, который описывает, как сравнивать с 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(), чтобы сравнить последовательности, понимая, что он не даст никакой информации о , элементы которых не равны.

0 голосов
/ 28 июня 2010

Как говорит Л.Бушкин, Assert.AreEqual просто вызовет Equals для последовательностей.

Вы можете использовать метод расширения SequenceEqual, хотя:

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

Это не даст много информации, если произойдет сбой.

Возможно, вы захотите использовать тестовый код , который мы написали для MoreLINQ , который был ориентирован на последовательность - если последовательности нет, это будет указывать, в чем они отличаются.(Я пытаюсь получить ссылку на рассматриваемый исходный файл, но мое сетевое соединение - мусор.)

...