C # Отличается от IEnumerable <T>с пользовательским IEqualityComparer - PullRequest
52 голосов
/ 11 января 2009

Вот что я пытаюсь сделать. Я запрашиваю XML-файл, используя LINQ to XML, что дает мне объект IEnumerable <T>, где T - это мой класс "Village", заполненный результатами этого запроса. Некоторые результаты дублируются, поэтому я хотел бы выполнить 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 ]

72 голосов
/ 11 января 2009

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

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

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

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

11 голосов
/ 16 декабря 2009

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

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

6 голосов
/ 24 ноября 2009

Или поменяйте строку

return alliances.Distinct(new AllianceComparer());

до

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