C# в отличие от интерфейса IEnumerable с пользовательский компаратор IEqualityComparer


вот что я пытаюсь сделать. Я запрашиваю XML-файл с помощью LINQ to XML, что дает мне IEnumerable<T> объект, где T - мой класс "деревня", заполненный результатами этого запроса. Некоторые результаты дублируются, поэтому я хотел бы выполнить Distinct () на объекте IEnumerable, например:

public IEnumerable<Village> GetAllAlliances()
{
    try
    {
        IEnumerable<Village> alliances =
             from alliance in xmlDoc.Elements("Village")
             where alliance.Element("AllianceName").Value != String.Empty
             orderby alliance.Element("AllianceName").Value
             select new Village
             {
                 AllianceName = alliance.Element("AllianceName").Value
             };

        // TODO: make it work...
        return alliances.Distinct(new AllianceComparer());
    }
    catch (Exception ex)
    {
        throw new Exception("GetAllAlliances", ex);
    }
}

поскольку компаратор по умолчанию не будет работать для объекта Village, я реализовал пользовательский, как показано здесь в AllianceComparer класс:

public class AllianceComparer : IEqualityComparer<Village>
{
    #region IEqualityComparer<Village> Members
    bool IEqualityComparer<Village>.Equals(Village x, Village y)
    {
        // Check whether the compared objects reference the same data.
        if (Object.ReferenceEquals(x, y)) 
            return true;

        // Check whether any of the compared objects is null.
        if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
            return false;

        return x.AllianceName == y.AllianceName;
    }

    int IEqualityComparer<Village>.GetHashCode(Village obj)
    {
        return obj.GetHashCode();
    }
    #endregion
}

метод Distinct () не работает, так как у меня есть точно такое же количество результатов с ним или без него. Другое дело, и я не знаю, возможно ли это обычно, но я не могу войти в AllianceComparer.Equals (), чтобы посмотреть, в чем может быть проблема.
Я нашел примеры этого в интернете, но я не могу заставить свою реализацию работать.

надеюсь, кто-то здесь может увидеть, что здесь может быть не так! Заранее спасибо!

3 51

3 ответа:

проблема с ваш GetHashCode. Вы должны изменить его, чтобы вернуть хэш-код AllianceName вместо.

int IEqualityComparer<Village>.GetHashCode(Village obj)
{
    return obj.AllianceName.GetHashCode();
}

дело в том, что если Equals возвращает true объекты должны иметь одинаковый хэш-код, который не относится к разного Village объекты с тем же AllianceName. Так как Distinct работает путем создания хэш-таблицы внутри, вы будете в конечном итоге с равными объектами, которые не будут сопоставлены вообще из-за различных хэш-кодов.

аналогично, чтобы сравнить два файла, если хэш двух файлов не совпадают, вам не нужно проверять сами файлы вообще. Они будет быть разные. В противном случае вы будете продолжать проверять, действительно ли они одинаковы или нет. Это именно то, что хэш-таблица, что Distinct использует себя ведет.

return alliances.Select(v => v.AllianceName).Distinct();

что бы вернуть IEnumerable<string> вместо IEnumerable<Village>.

или изменить строку

return alliances.Distinct(new AllianceComparer());

до

return alliances.Select(v => v.AllianceName).Distinct();