Подтвердить что ты не робот

Почему нет. Кроме того, (LINQ) правильно сравнивает вещи? (используя IEquatable)

У меня есть две коллекции собственных объектов ссылочного типа, которые я написал для себя свой метод IEquatable.Equals, и я хочу иметь возможность использовать методы LINQ для них.

Итак,

List<CandyType> candy = dataSource.GetListOfCandy();
List<CandyType> lollyPops = dataSource.GetListOfLollyPops();
var candyOtherThanLollyPops = candy.Except( lollyPops );

В соответствии с документацией. Кроме того, если не передавать IEqualityComparer, это должно привести к тому, что EqualityComparer.Default используется для сравнения объектов. И документация для сравнения по умолчанию:

"Свойство Default проверяет, реализует ли тип T общий универсальный интерфейс System.IEquatable, и если это так возвращает EqualityComparer, который использует эту реализацию. В противном случае он возвращает EqualityComparer, который использует переопределения Object.Equals и Object.GetHashCode, предоставленные T."

Итак, поскольку я реализую IEquatable для своего объекта, он должен использовать это и работать. Но это не так. Он не работает, пока я не переопределяю GetHashCode. Фактически, если я устанавливаю точку прерывания, мой метод IEquatable.Equals никогда не будет выполнен. Это заставляет меня думать, что он идет с планом Б в соответствии с его документацией. Я понимаю, что переопределение GetHashCode - это хорошая идея, так или иначе, и я могу заставить это работать, но я расстроен тем, что он ведет себя так, что не соответствует тому, что заявлено в его собственной документации.

Почему он не делает то, что он сказал? Спасибо.

4b9b3361

Ответ 1

После расследования выясняется, что все не так плохо, как я думал. В основном, когда все реализовано правильно (GetHashCode и т.д.), Документация верна, а поведение корректно. Но если вы попытаетесь сделать что-то вроде реализации IEquatable, то ваш метод Equals никогда не будет вызван (это, вероятно, связано с тем, что GetHashCode не реализован должным образом). Таким образом, хотя документация технически неверна, она ошибочна только в некоторой ситуации, которую вы никогда не захотите делать (если это исследование меня чему-то научило, то, что IEquatable является частью целого набора методов, которые вы должны реализовать атомарно ( по договоренности, а не по правилам, к сожалению)). Хорошие источники по этому поводу:

Есть ли полная ссылка на реализацию IEquatable?

http://msdn.microsoft.com/en-us/library/ms131190.aspx

http://blogs.msdn.com/irenak/archive/2006/07/18/669586.aspx

Ответ 2

Интерфейс IEqualityComparer<T> имеет следующие два метода:

bool Equals(T x, T y);
int GetHashCode(T obj);

Хорошая реализация этого интерфейса будет таким образом реализовывать оба. Метод расширения Linq За исключением использования хеш-кода, чтобы использовать словарь или задавать внешний вид внутри, чтобы выяснить, какие объекты пропустить, и, следовательно, требует правильной реализации GetHashCode.

К сожалению, когда вы используете EqualityComparer<T>.Default, этот класс не обеспечивает хорошую реализацию GetHashCode сам по себе и полагается на объект, тип T, чтобы предоставить эту часть, когда обнаруживает, что объект реализует IEquatable<T>.

Проблема здесь в том, что IEquatable<T> на самом деле не объявляет GetHashCode, поэтому гораздо легче забыть реализовать этот метод правильно, в отличие от метода Equals, который он объявляет.

Итак, у вас есть два варианта:

  • Обеспечьте правильную реализацию IEqualityComparer<T>, которая реализует как Equals, так и GetHashCode
  • Убедитесь, что в дополнение к реализации IEquatable<T> на вашем объекте реализуйте надлежащий GetHashCode, а также

Ответ 3

Опасность угадывания - это разные классы? Я думаю, что по умолчанию IEquatable работает только с тем же классом. Поэтому он может вернуться к методу Object.Equal.

Ответ 4

Я написал GenericEqualityComparer, который будет использоваться на лету для этих типов методов: "Разные", "Кроме", "Пересечь" и т.д.

Используйте следующим образом:

var results = list1.Except(list2, new GenericEqualityComparer<MYTYPE>((a, b) => a.Id == b.Id // OR SOME OTHER COMPARISON RESOLVING TO BOOLEAN));

Здесь класс:

public class GenericEqualityComparer<T> : EqualityComparer<T>
{
    public Func<T, int> HashCodeFunc { get; set; }

    public Func<T, T, Boolean> EqualityFunc { get; set; }

    public GenericEqualityComparer(Func<T, T, Boolean> equalityFunc)
    {
        EqualityFunc = equalityFunc;
        HashCodeFunc = null;
    }

    public GenericEqualityComparer(Func<T, T, Boolean> equalityFunc, Func<T, int> hashCodeFunc) : this(equalityFunc)
    {
        HashCodeFunc = hashCodeFunc;
    }

    public override bool Equals(T x, T y)
    {
        return EqualityFunc(x, y);
    }

    public override int GetHashCode(T obj)
    {
        if (HashCodeFunc == null)
        {
            return 1;
        }
        else
        {
            return HashCodeFunc(obj);
        }
    }
}