Reputation: 20120
so I'm trying to make this work and I can't seem to know why it doesn't work
demo code;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var myVar = new List<parent >();
myVar.Add(new parent() { id = "id1", blah1 = "blah1", c1 = new child() { blah2 = "blah2", blah3 = "blah3" } });
myVar.Add(new parent() { id = "id1", blah1 = "blah1", c1 = new child() { blah2 = "blah2", blah3 = "blah3" } });
var test = myVar.Distinct();
Console.ReadKey();
}
}
public class parent : IEquatable<parent>
{
public String id { get;set;}
public String blah1 { get; set; }
public child c1 { get; set; }
public override int GetHashCode()
{
unchecked // Overflow is fine, just wrap
{
int hash = 17;
// Suitable nullity checks etc, of course :)
hash = hash * 23 + id.GetHashCode();
hash = hash * 23 + blah1.GetHashCode();
hash = hash * 23 + (c1 == null ? 0 : c1.GetHashCode());
return hash;
}
}
public bool Equals(parent other)
{
return object.Equals(id, other.id) &&
object.Equals(blah1, other.blah1) &&
object.Equals(c1, other.c1);
}
}
public class child : IEquatable<child>
{
public String blah2 { get; set; }
public String blah3 { get; set; }
public override int GetHashCode()
{
unchecked // Overflow is fine, just wrap
{
int hash = 17;
// Suitable nullity checks etc, of course :)
hash = hash * 23 + blah2.GetHashCode();
hash = hash * 23 + blah3.GetHashCode();
return hash;
}
}
public bool Equals(child other)
{
return object.Equals(blah2, other.blah2) &&
object.Equals(blah3, other.blah3);
}
}
}
anyone could spot my error(s) ?
Upvotes: 7
Views: 8293
Reputation: 658
There are several things to get right here. If I'm going to implement any aspect of equality in a class such as GetHashCode
, overriding == or IEquatable
, I always use the following pattern.
IEquatable<T>
which means implementing Equals(T)
So, if I had a class named ExpiryMonth with properties Year and Month, this is how that implementation would look. It's a fairly mindless task now to adapt for other types of classes.
I have based this pattern on several other stackoverflow answers which all deserve credit, but which I haven't tracked along the way.
By always implementing all of these elements together, it ensures proper equality operations in a variety of contexts including dictionaries and Linq operations.
public static bool operator !=(ExpiryMonth em1, ExpiryMonth em2)
{
if (((object)em1) == null || ((object)em2) == null)
{
return !Object.Equals(em1, em2);
}
else
{
return !(em1.Equals(em2));
}
}
public static bool operator ==(ExpiryMonth em1, ExpiryMonth em2)
{
if (((object)em1) == null || ((object)em2) == null)
{
return Object.Equals(em1, em2);
}
else
{
return em1.Equals(em2);
}
}
public bool Equals(ExpiryMonth other)
{
if (other == null) { return false; }
return Year == other.Year && Month == other.Month;
}
public override bool Equals(object obj)
{
if (obj == null) { return false; }
ExpiryMonth em = obj as ExpiryMonth;
if (em == null) { return false; }
else { return Equals(em); }
}
public override int GetHashCode()
{
unchecked // Overflow is not a problem
{
var result = 17;
result = (result * 397) + Year.GetHashCode();
result = (result * 397) + Month.GetHashCode();
return result;
}
}
Upvotes: 1
Reputation: 887469
You need to override the Equals(object)
method:
public override bool Equals(object obj) {
return Equals(obj as parent);
}
The object.Equals
method (unlike EqualityComparer<T>.Default
) does not use the IEquatable
interface. Therefore, when you write object.Equals(c1, other.c1)
, it doesn't call your Child.Equals(Child)
method.
You don't absolutely need to do that for parent
as well, but you really should.
Upvotes: 5
Reputation: 20054
Either you do what SLaks suggests, or you use EqualityComparer<child>.Default
in your parent
class to use your IEquatable<child>
implementation:
public bool Equals(parent other)
{
return object.Equals(id, other.id) &&
object.Equals(blah1, other.blah1) &&
EqualityComparer<child>.Default.Equals(c1, other.c1);
}
Upvotes: 3
Reputation: 220
When adding the calculating the hash you might want to try something like
hash ^= id.GetHashCode();
Not sure if that is what is causing your issue.
Upvotes: 0