Dork
Dork

Reputation: 1886

Distinct method doesn't work for class with overriden Equals

I created class with overriden Equals. The problem is that Distinct method doesn't work for my class.

class MyClass
{
    public int Item1 { get; private set; }
    public int Item2 { get; private set; }

    public MyClass(int item1, int item2)=>(Item1,Item2)=(item1,item2); 


    public override bool Equals(object obj)
    {
        var other = obj as MyClass;

        if (other == null)
        {
            return false;
        }

        return (this.Item1 == other.Item1 && this.Item2 == other.Item2);
    }
}

class Program
{
    static void Main(string[] args)
    {
        MyClass x = new MyClass(1, 0);
        MyClass y = new MyClass(1, 0);
        var list = new List<MyClass>();
        list.Add(x);
        list.Add(y);


        bool b = x.Equals(y));  //True
        var distincts = list.Distinct(); //Doesn't work, contains both
    }
}

How can I fix that and why it doesn't use my Equals in Distinct?

Upvotes: 2

Views: 929

Answers (3)

adjan
adjan

Reputation: 13652

You have to override GetHashCode as well:

public override int GetHashCode()
{
    return Item1; // or something
}

Distinct first compares the hashcodes, which should be computed faster than the actual Equals. Equals is only further evaulated if the hashcodes are equal for two instances.

Upvotes: 2

Sweeper
Sweeper

Reputation: 271735

Distinct docs:

Returns distinct elements from a sequence by using the default equality comparer to compare values.

Let's see what the default equality comparer does:

The Default property checks whether type T implements the System.IEquatable<T> interface and, if so, returns an EqualityComparer<T> that uses that implementation. Otherwise, it returns an EqualityComparer<T> that uses the overrides of Object.Equals and Object.GetHashCode provided by T.

So basically, to make this work, you either:

  • implement GetHashCode as well
  • implement IEquatable<T>
  • Call the overload of Distinct that accepts a custom equality comparer.

If I were you, I would choose the second one because you need to change the least of your code.

class MyClass: IEquatable<MyClass> {
    ...

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

        return (this.Item1 == obj.Item1 && this.Item2 == obj.Item2);
    }
}

Upvotes: 3

Jenish Rabadiya
Jenish Rabadiya

Reputation: 6766

You need to implement IEquatable<MyClass> in MyClass and provide your own implementation of GetHashCode and Equals method.

see this for more information.

class MyClass
{
    public int Item1 { get; private set; }
    public int Item2 { get; private set; }

    public MyClass(int item1, int item2)=>(Item1,Item2)=(item1,item2); 


    public override bool Equals(object obj)
    {
        var other = obj as MyClass;

        if (other == null)
        {
            return false;
        }

        return (this.Item1 == other.Item1 && this.Item2 == other.Item2);
    }

    public override int GetHashCode()
    {

        return this.Item1;
    }
}

Upvotes: 1

Related Questions