Search code examples
c#exceptiequalitycomparer

List<T>.Except(List<T>) not working as expected


I have two lists of the same type.

One list is a deserialized json object loaded from a file and casted to the correct type.

The other is a list created of objects of the same type.

When I do List2.Except(List1) I expect to see everything in List2 that isn't in List1

In this case, List2 will have everything that List1 has and nothing more, so I expect to see a resulting Enumerable<T> of size 0, however I'm just seeing the entirety of List2 as if the comparison never happened.

IEnumerable<Review> newReviews = reviews.Except(savedRvReviews, new ReviewComparer());

I also tried using an EqualityComparer, code below:

public class ReviewComparer : IEqualityComparer<Review>
{
    public bool Equals(Review x, Review y)
    {
        return x.ReviewID == y.ReviewID;
    }

    public int GetHashCode(Review rv)
    {
        return EqualityComparer<Review>.Default.GetHashCode(rv);
    }
}

and got the same results.

Iterating through List2 and searching List1 via a LINQ query, I get the expected results of 0.

foreach (Review s in reviews)
{
    var m = from k in savedRvReviews
    where k.ReviewID == s.ReviewID
    select k;

    if (m.Count() == 0)
    {
        // Do something with new reviews
    }
}

Code for Review type:

public class Review : INotifyPropertyChanged
{

    public event PropertyChangedEventHandler PropertyChanged;

    [Browsable(false)]
    public string ReviewID { get; set; }
    public string ProductID { get; set; }
    public DateTime DateSubmitted { get; set; }
    public string ReviewTitle { get; set; }

    [Browsable(false)]
    public string ReviewDescription { get; set; }
    public int ReviewMark { get; set; }

    [Browsable(false)]
    public bool Approved { get; set; }
}

Solution

  • You have written a comparer where two reviews are equal when their IDs are equal, but you have not modified the hashcode. Two reviews that are equal should produce the same hashcode and your's don't.

    the easiest way to make it happen would be to return the same hashcode for reviews that are equal. For example by using the hashcode of the only thing that makes two reviews equal, the ID:

    public class ReviewComparer : IEqualityComparer<Review>
    {
        public bool Equals(Review x, Review y)
        {
            return x.ReviewID == y.ReviewID;
        }
    
        public int GetHashCode(Review rv)
        {
            return rv.ReviewID.GetHashCode();
        }
    }