I want to use the HashSet.Contains method because its super fast.
var hashset = new HashSet<Customer>(customers, new CustomerComparer());
var found = hashset.Contains(new Customer{ Id = "1234", Name = "mit" }); // "mit" instead of an equals "smith" in the comparer.
I am searching for multiple properties on the customer object.
I have to implement the IEqualityComparer interface like:
public class CustomerComparer : IEqualityComparer<Customer>
{
public bool Equals(Customer x, Customer y)
{
return x.Id == y.Id && x.Name.Contains(y.Name);
}
public int GetHashCode(Customer obj)
{
return obj.Id.GetHashCode() ^ obj.Name.GetHashCode();
}
}
Why is the Equals method never hit when I do NOT use an Equals method inside the CustomerComparer Equals method like the .Contains?
The way you have implemented the equality comparer can not work properly. The reason is how the hash set and the equality comparer work internally. When a Dictionary
or a HashSet
does a comparison of items, it will first call GetHashCode
on both items. Only if these hash codes match, it will confirm the exact match with a subsequent call to Equals
to avoid false matches in case of a hash code collision. If you use your example, (x.Name = "smith"
and y.Name = "mit"
), the GetHashCode
method will return different hash codes for each item and Equals
is never called.
The solution in this case is to only use the Id
for hash code creation. That would degrade performance a bit because you would have to call Equals
more often to resolve collision, but that's the price you have to pay:
public int GetHashCode(Customer obj)
{
return obj.Id.GetHashCode() ;
}
What you should also consider that you have no guarantee whether your existing item will be x
or y
. So you have to use Contains
in both directions:
public bool Equals(Customer x, Customer y)
{
return x.Id == y.Id && (x.Name.Contains(y.Name) || y.Name.Contains(x.Name));
}