Distinct () не работает должным образом и не вызывает метод Equals в списке пользовательских объектов - PullRequest
0 голосов
/ 18 марта 2020

Я создал свое собственное определение Tuple, чтобы у меня было собственное определение метода Equals, в котором (1, 3) равно (3, 1). Код приведен ниже.

public struct Tuple : IEquatable<object>
{
    public int Item1 { get; set; }
    public int Item2 { get; set; }


    public Tuple(int item1, int item2)
    {
        this.Item1 = item1;
        this.Item2 = item2;
    }

    public override bool Equals(object obj)
    {
        if (obj == null)
        {
            return false;
        }

        if (Object.ReferenceEquals(this, obj))
            return true;

        Tuple other;
        if (obj is Tuple)
        {
            other = (Tuple)obj;
        }
        else
        {
            return false;
        }

        if (this.Item1 == other.Item1 && this.Item2 == other.Item2
          || this.Item1 == other.Item2 && this.Item2 == other.Item1)
        {
            return true;
        }

        return false;
    }

    public override int GetHashCode()
    {
        int hash = 13;
        hash = (hash * 7) + Item1.GetHashCode();
        hash = (hash * 7) + Item2.GetHashCode();
        return hash;
    }

    public static bool operator ==(Tuple left, Tuple right)
    {
        return left.Equals(right);
    }

    public static bool operator !=(Tuple left, Tuple right)
    {
        return !(left == right);
    }
}

Но когда я запускаю отчетливый список Список кортежей, он не использует мое определение Равных. Например, для переменной vertices, как показано ниже:

List<Tuple> vertices

Я делаю:

var distinctVertices = vertices.Distinct();

И она проверяет различные значения, но не проходит через мое определение Equals Таким образом, не работает в случае, который я описал выше. Есть идеи, что я могу делать не так?

Ответы [ 2 ]

4 голосов
/ 18 марта 2020

Во-первых, вы, вероятно, хотите реализовать IEquatable<Tuple>, а не IEquatable<object> - Tuple : IEquatable<Tuple>.


Во-вторых, равенство на изменчивом struct - ужасная идея; Я настоятельно рекомендую вам использовать:

public readonly struct Tuple : IEquatable<Tuple>
{
    public int Item1 { get; }
    public int Item2 { get; }
    public Tuple(int item1, int item2)
    {
        Item1 = item1;
        Item2 = item2;
    }
    ...
}

В-третьих, ваши Equals и GetHashCode должны согласиться. Если порядок не имеет значения до Equals, то не должен иметь значения до GetHashCode. Рассмотрим просто:

public override int GetHashCode() => Item1 ^ Item2;

или что-то еще, не зависящее от порядка.


Наконец, ваш Equals, вероятно, может быть упрощен:

public override bool Equals(object obj) => obj is Tuple other && Equals(other);
public bool Equals(Tuple other)
    => (Item1 == other.Item1 && Item2 == other.Item2)
    || (Item1 == other.Item2 && Item2 == other.Item1);
1 голос
/ 18 марта 2020

Посмотрите на это примечание в документации Distinct :

Этот метод реализован с использованием отложенного выполнения. Немедленное возвращаемое значение - это объект, в котором хранится вся информация, необходимая для выполнения действия. Запрос, представленный этим методом, не выполняется до тех пор, пока объект не будет перечислен путем непосредственного вызова его метода GetEnumerator или использования foreach в Visual C# или For Each в Visual Basi c.

This означает, что пока вы не перечислите результат, ничего не произойдет с точки зрения фильтрации последовательности.

Если вы сделаете foreach или добавите ToList к результату, то есть:

var distinctVertices = vertices.Distinct().ToList();

или

foreach(var vertice in distinctVertices)
{
    // optionally do something here
}

Тогда вы увидите Equals метод выполнения.

...