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 (6)

  1. Joe Enos says:

    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.

  2. 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.

  3. Joe Enos says:

    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.

  4. DoctaJonez says:

    I totally agree with you Joe.  If you look at a line of code that says obj1 == obj2, you don’t know from looking at it whether it is a reference equals or a value equals.  This gives the potential for subtle errors to find their way into your code.  

    It would have been much better if only value types could override the equals operator.

    If it was a method call such as obj1.Equals(obj2) then the developer will know immediatley what is going on.  

    I prefer code that conveys exactly what is happening without requiring an interrogation via the debugger.

    Incidentally I also think that properties are somewhat of a pain for the same reasons (although I do like properties), they have the potential for severe misuse.  You cannot visually tell the difference between a property and variable, from outside of the IDE (depending on your coding conventions).

    The real question is:  Should a language feature be scrapped just because it has a potential for misuse?  

    Just because one guy might write scrappy code using a specific language feature doesn’t mean that it should be left out of the language.

    It is always nice to have options, even if you currently have no intention of using them. 🙂

  5. @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.  

  6. @DoctaJonez

    I think you have to weigh carefully use vs. misuse of a feature.  Unfortunately virtually any feature is potential for abuse.  Take extension methods for example.  We could use them to solve this equality problem in a somewhat abusive fashion.

    static bool SafeEquals<T>(this T lhs, T rhs) {

    return EqualityComparer<T>.Default.Equals(lhs,rhs);


    Now I can call someObj.SafeEquals(other) wherever I please and it will take care of all of the null checking and what have you.  Unfortuantely this also like I’m potentially not taking care of a null check to a casual observer.  

    Operator overloading, and hence ==, definately falls into this category.  Easy to abuse, but incredibly powerful when used appropriately.  

    Personally I like to err on the side of giving users more power.  Even the safest language can be rendered unreable by a skilled (or unskilled) programmer.  Making a language less powerful will just upset the skilled users who don’t want to write obfuscated code.