title: Equality and polymorphism
body: <p>With two immutable classes Base and Derived (which derives from Base) I want to define Equality so that</p>
  
  <ul>
  <li><p>equality is always polymorphic - that is <code>((Base)derived1).Equals((Base)derived2)</code> will call <code>Derived.Equals</code></p></li>
  <li><p>operators <code>==</code> and <code>!=</code> will call <code>Equals</code> rather than <code>ReferenceEquals</code> (value equality)</p></li>
  </ul>
  
  <p>What I did:</p>
  
  <pre><code>class Base: IEquatable&lt;Base&gt; {
    public readonly ImmutableType1 X;
    readonly ImmutableType2 Y;
  
    public Base(ImmutableType1 X, ImmutableType2 Y) { 
      this.X = X; 
      this.Y = Y; 
    }
  
    public override bool Equals(object obj) {
      if (object.ReferenceEquals(this, obj)) return true;
      if (obj is null || obj.GetType()!=this.GetType()) return false;
  
      return obj is Base o 
        &amp;&amp; X.Equals(o.X) &amp;&amp; Y.Equals(o.Y);
    }
  
    public override int GetHashCode() =&gt; HashCode.Combine(X, Y);
  
    // boilerplate
    public bool Equals(Base o) =&gt; object.Equals(this, o);
    public static bool operator ==(Base o1, Base o2) =&gt; object.Equals(o1, o2);
    public static bool operator !=(Base o1, Base o2) =&gt; !object.Equals(o1, o2);    }
  </code></pre>
  
  <p>Here everything ends up in <code>Equals(object)</code> which is always polymorphic so both targets are achieved.</p>
  
  <p>I then derive like this:</p>
  
  <pre><code>class Derived : Base, IEquatable&lt;Derived&gt; {
    public readonly ImmutableType3 Z;
    readonly ImmutableType4 K;
  
    public Derived(ImmutableType1 X, ImmutableType2 Y, ImmutableType3 Z, ImmutableType4 K) : base(X, Y) {
      this.Z = Z; 
      this.K = K; 
    }
  
    public override bool Equals(object obj) {
      if (object.ReferenceEquals(this, obj)) return true;
      if (obj is null || obj.GetType()!=this.GetType()) return false;
  
      return obj is Derived o
        &amp;&amp; base.Equals(obj) /* ! */
        &amp;&amp; Z.Equals(o.Z) &amp;&amp; K.Equals(o.K);
    }
  
    public override int GetHashCode() =&gt; HashCode.Combine(base.GetHashCode(), Z, K);
  
    // boilerplate
    public bool Equals(Derived o) =&gt; object.Equals(this, o);
  }
  </code></pre>
  
  <p>Which is basically the same except for one gotcha - when calling <code>base.Equals</code> I call <code>base.Equals(object)</code> and not <code>base.Equals(Derived)</code> (which will cause an endless recursion).</p>
  
  <p>Also <code>Equals(C)</code> will in this implementation do some boxing/unboxing but that is worth it for me.</p>
  
  <p>My questions are - </p>
  
  <p>First is this correct ? my (<a href="https://dotnetfiddle.net/JqwfEe" rel="noreferrer">testing</a>) seems to suggest it is but with C# being so difficult in equality I'm just not sure anymore .. are there any cases where this is wrong ?</p>
  
  <p>and Second - is this good ? are there better cleaner ways to achieve this ?</p>