Yet another rule for Equality
"If you implement equality in a child class, including operators, you must implement the equality operators in the base class."
Unfortunately this is another case of learn the hard way but makes sense when you think about it. The below code snippet is an example of the problem that I hit. Even though I have equality properly defined in Child, the equality check goes through Parent. As such the C# compiler will perform the default comparison which is reference equality.
The simple fix is to add the operator ==/!= definitions to Parent which call through EqualityComparer<Parent>.Default. This will end up calling obj.Equals and equality will function correctly.
While this is intuitive when you think about it, it's an easy trap to fall into. It would be nice if there was a Compiler/FXCop warning here.
class Parent {
}
class Child : Parent{
public readonly int Field1;
public Child(int value) {
Field1 = value;
}
public override int GetHashCode() {
return Field1;
}
public override bool Equals(object obj) {
var other = obj as Child;
if (other == null) {
return false;
}
return other.Field1 == Field1;
}
public static bool operator ==(Child left, Child right) {
return EqualityComparer<Child>.Default.Equals(left, right);
}
public static bool operator !=(Child left, Child right) {
return !EqualityComparer<Child>.Default.Equals(left, right);
}
}
class Program {
static void Main(string[] args) {
Child child1 = new Child(42);
Child child2 = new Child(42);
Parent parent1 = child1;
Parent parent2 = child2;
bool isChildEqual = child2 == child1; // True
bool isParentEqual = parent1 == parent2; // False
}
}
Comments
Anonymous
July 11, 2008
This is the reason why I believe any reference type should not override ==/!=. In my opinion, two reference types that are not the same object should always return false for ==, even if they are functionally equivalent. If you want to compare reference types based on their "value", I would always use Equals or Compare/CompareTo, after implementing the appropriate generic interfaces (IEquatable<T>/IComparable<T>). I would have actually preferred a feature in the framework that says that only value types can override ==/!= - this inheritance scenario doesn't apply. But I'm sure there are plenty of people out there who disagree with me.Anonymous
July 11, 2008
I think it's perfectly OK for reference types to implement equality and override ==/!=. I think the bug lies in that C# does not separate reference and value equality. Take VB for instance. It has a hard separation between value and reference equality (Is vs. Operator = ). If C# had this separation this wouldn't be quite the problem.Anonymous
July 11, 2008
Since you brought up VB: I can have a class that overrides = (op_Equality) and <> (op_Inequality). In that scenario, with VB code, I can say "obj1 Is obj2" if I want reference equality or "obj1 = obj2" if I want my custom equality. If that same assembly is referenced from C#, my C# code cannot determine reference equality - if I say "obj1 == obj2", it will call the custom method, not reference equality. I believe your viewpoint is that this is a C# flaw, since VB can handle it just fine. My opinion is that there is already Equals(object) and Equals<T>(T) - that's enough for me to determine logical equivalence - I don't need another non-reference equality to add to the confusion. I do enjoy the discussion - it really gets me thinking about best practices.Anonymous
July 14, 2008
The comment has been removedAnonymous
July 15, 2008
@Joe I agree that we have Equals() so why bother with yet another way of doing the same thing? For me it's simple laziness. The problem with .Equals() is that you have to guarantee that the LHS of the equation is non-null otherwise equality turns into a null reference exception where I would prefer null simply means not-equal (unless of course they are both null). Truthfully I wish there was one operator which would 1) do proper null checking and 2) call LHS.Equals(RHS). You can be this with EqualityComparer<T>.Default.Equals() but it's quite a mouthful to type.Anonymous
July 15, 2008
The comment has been removed