i need to perform simple set-operations in linq (for example Union, Except and Intersect)
class Person {
public int Id { get; set; }
public string Name { get; set; }
public Person() { }
public Person(int id, string name) {
Id = id; Name = name;
}
}
Comparer implementation:
class PersonComparer : IEqualityComparer<Person> {
public bool Equals(Person x, Person y) {
return x.Id == y.Id;
}
public int GetHashCode(Person p) {
return p.GetHashCode();
}
}
Populating lists:
var list1 = new List<Person>();
list1.Add(new Person(1, "John"));
list1.Add(new Person(2, "Peter"));
list1.Add(new Person(3, "Mike"));
var list2 = new List<Person>();
list2.Add(new Person(2, "Peter"));
list2.Add(new Person(3, "Mike"));
list2.Add(new Person(4, "Fred"));
var comparer = new PersonComparer();
var list3 = list1.Intersect(list2, comparer).ToList(); // **Empty List**
var list4 = list1.Except(list2, comparer).ToList(); // **"John", "Peter", "Mike"**
It seems that my comparer does not work. Why?
The problem is your implementation of GetHashCode(Person p)
. As noted in MSDN:
Implementations are required to ensure that if the
Equals
method returns true for two objectsx
andy
, then the value returned by theGetHashCode
method forx
must equal the value returned fory
.
In your case, p.GetHashCode()
may return a different value for each p
in memory, even if they have the same Id
—that is, two different instances of Person
may have the same Id
but different hash codes—so this isn't sufficient to satisfy the requirement noted above for a proper implementation of GetHashCode(Person p)
. Instead, use something like this:
public int GetHashCode(Person p) {
return p.Id;
}