11

I've implemented IEqualityComparer and IEquatable (both just to be sure), but when I call the Distinct() method on a collection it does not call the methods that come with it. Here is the code that I execute when calling Distinct().

ObservableCollection<GigViewModel> distinctGigs = new ObservableCollection<GigViewModel>(Gigs.Distinct<GigViewModel>());
return distinctGigs;

I want to return an ObservableCollection that doesn't contain any double objects that are in the 'Gigs' ObservableCollection.

I implement the interfaces like this on the GigViewModel class:

public class GigViewModel : INotifyPropertyChanged, IEqualityComparer<GigViewModel>, IEquatable<GigViewModel>
{
    ....
}

And override the methods that come with the interfaces like so:

public bool Equals(GigViewModel x, GigViewModel y)
{          
    if (x.Artiest.Naam == y.Artiest.Naam)
    {
        return true;
    }
    else
    {
        return false;
    }
 }

 public int GetHashCode(GigViewModel obj)
 {
     return obj.Artiest.Naam.GetHashCode();
 }

 public bool Equals(GigViewModel other)
 {
     if (other.Artiest.Naam == this.Artiest.Naam)
     {
         return true;
     }
     else
     {
         return false;
     }
 }

Thanks for all the help I'm getting. So I've created a seperate class that implements IEqualityComparer and passed it's instance into the disctinct method. But the methods are still not being triggered.

EqualityComparer:

class GigViewModelComparer : IEqualityComparer<GigViewModel>
{
    public bool Equals(GigViewModel x, GigViewModel y)
    {

        if (x.Artiest.Naam == y.Artiest.Naam)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    public int GetHashCode(GigViewModel obj)
    {
        return obj.Artiest.Naam.GetHashCode();
    }
}

The Distinct() call:

 GigViewModelComparer comp = new GigViewModelComparer();
 ObservableCollection<GigViewModel> distinctGigs = new ObservableCollection<GigViewModel>(Gigs.Distinct(comp));
 return distinctGigs;

EDIT2:

The GetHashCode() method DOES get called! After implementing the new class. But the collection still contains duplicates. I have a list of 'Gigs' that contain an 'Artiest' (or Artist) object. This Artist has a Naam property which is a String (Name).

Tim Kranen
  • 4,202
  • 4
  • 26
  • 49
  • Does it call GetHashCode? – Liath Jan 07 '14 at 14:33
  • Are you _certain_ the method is not being called, or are you just seeing "duplicates" in your _results_. Are you certain it's not differences in whitespace or character casing? – D Stanley Jan 07 '14 at 15:24
  • I'm 100% certain. I've set breakpoints in the methods, they aren't triggered. And the duplicates are in the result. – Tim Kranen Jan 07 '14 at 15:26
  • I don't see the `override` keyword in your code examples. **You need the `override` keyword to override**, this isn't java. Also you shouldn't implement `IEqualityComparer` on the class itself. You should either implement `IEqualityComparer` on an independent class (which you then pass explicitly to `Distinct`) or you should override `object.Equals` and `object.GetHashCode` on your class, and optionally implement `IEquatable`. – CodesInChaos Jan 07 '14 at 15:36
  • @CodesInChaos Please view my edits. When I do try to use the override keywords, Visual Studio states that no suitable method has been found to override. – Tim Kranen Jan 07 '14 at 15:41
  • You can't use `override` to implement an interface, but you must use it to override `object.GetHashCode` and `object.Equals` – CodesInChaos Jan 07 '14 at 15:42
  • I'm sorry I don't follow. I've tried to change the method into 'private int override GetHashCode(obj obj) but that did not work. – Tim Kranen Jan 07 '14 at 15:46
  • 2
    Oh my, I am stupid. I should've overwritten the GetHashCode() on the GigViewModel object instead! That eventually worked! – Tim Kranen Jan 07 '14 at 15:49
  • 1
    @CodesInChaos I think you should leave an answer that I could accept stating that I should override the GetHashCode() method in the GigViewModel. – Tim Kranen Jan 07 '14 at 15:51
  • @TimKranen That is one of the two solutions described in my answer. – Servy Jan 07 '14 at 15:57
  • @Servy Ow wow. I did not see that, I chose the IEqualityComparer solution. But I guess I used a bit of both. You're answer is clearly the best and precise one. Thanks! – Tim Kranen Jan 07 '14 at 15:59

2 Answers2

9

So you had the object that itself is being compared implement both IEquatable as well as IEqualityComparer. That generally doesn't make sense. IEquatable is a way of saying an object can compare itself to something else. IEqualityComparer is a way of saying it can compare two different things you give it to each other. You generally want to do one or the other, not both.

If you want to implement IEquatable then the object not only needs to have an Equals method of the appropriate signature, but it needs to override GetHashCode to have a sensible implementation for the given definition of equality. You didn't do that. You created GetHashCode method that takes an object as a parameter, but that's the overload used for IEqualityComparer. You need to override the parameter-less version when using IEquatable (the one defined in Object).

If you want to create a class that implements IEqualityComparer you need to pass the comparer to the Distinct method. Since you've defined the object as its own comparer you'd need to pass in some instance of this object as the second parameter. Of course, this doesn't really make a whole lot of sense this way; so it would be better, if you go this route, to pull out the two methods that go with IEqualityComparer into a new type, and create an instance of that type to the Distinct method. If you actually passed an object with those definitions in as a comparer, it'd work just fine.

Servy
  • 202,030
  • 26
  • 332
  • 449
1

Following MSDN's advice, you'd be best off creating a separate class for your equality comparisons:

We recommend that you derive from the EqualityComparer class instead of implementing the IEqualityComparer interface, because the EqualityComparer class tests for equality using the IEquatable.Equals method instead of the Object.Equals method. This is consistent with the Contains, IndexOf, LastIndexOf, and Remove methods of the Dictionary class and other generic collections.

So, create a class, GigViewModelComparer, that derives from EqualityComparer and put your Equals and GetHashCode methods there.

Then, pass in an instance of that new comparer class in your call to Gigs.Distinct(new GigViewModelComparer()) and it should work. Follow along in the example in the MSDN link I provided above.

I've never seen somebody implement IEqualityComparer in the same class as the type of objects the collection in question contains, that is probably at least part of your problem.

Sven Grosen
  • 5,616
  • 3
  • 30
  • 52