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
Object.Equals
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();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With