IEquatables implementation only called if the base Equals is overridden
I have the following class
class Product : IEquatable<Product>
{
public Guid Id { get; set; }
public bool Equals(Product other)
{
return Id.Equals(other.Id);
}
}
If i try and create a unique list of the items of a list as follows
Guid a = Guid.NewGuid();
List<Product> listA = new List<Product>();
listA.Add(new Product(){Id = a});
List<Product> listB = new List<Product>();
listB.Add(new Product()
{
Id = a
});
Debug.Assert(listA.Union(listB).Count()==1);
two items are returned, this occurs until I override the object.Equals method, once i do this and my code is as follows
class Product : IEquatable<Product>
{
public Guid Id { get; set; }
public bool Equals(Product other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return other.Id.Equals(Id);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof (Product)) return false;
return Equals((Product) obj);
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
}
my IEquatable Equals method is now called, but only if i override the base method, furthermore if i put a breakpoint on the object equals method it is never called.
Why is this?
----UPDATE
So with the product class
开发者_高级运维class Product : IEquatable<Product>
{
public Guid Id
{
get;
set;
}
public bool Equals(Product other)
{
return Id.Equals(other.Id);
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
}
If GetHashCode is removed, The IEquatable implementation of Equals is never hit I understand you should generally implement Equals and GetHashCode together, is this why?
The problem is in the original implementation you're not overriding the GetHashcode
method. Under the hood Union
uses a Set<T>
style structure to remove duplicates. This structure puts objects into buckets based on the value returned from GetHashCode
. If the hash code doesnt't match up between equal objects (which it must do) then they can potentially be put in different buckets and never compared with Equals
In general if you implement IEquatable<T>
you should always
- Override
Object.Equals
- Override
Object.GetHashCode
Not doing both will land you in situations like this.
Note, your implementation could be simplified a bit
class Product : IEquatable<Product>
{
public Guid Id { get; set; }
public bool Equals(Product other)
{
if (ReferenceEquals(null, other)) {
return false;
}
return other.Id == this.Id;
}
public override bool Equals(object obj)
{
return Equals(obj as Product);
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
}
Documentation for Enumerable.Union says:
The default equality comparer, Default, is used to compare values of the types that implement the IEqualityComparer(Of T) generic interface. To compare a custom data type, you need to implement this interface and provide your own GetHashCode and Equals methods for the type.
You're implementing IEquatable
. You need to implement IEqualityComparer
.
精彩评论