Equality and polymorphism (3)

CSharp · Console

/Program.cs

using System;
using System.Immutable;


namespace System.Immutable {
  public interface IImmutableEquatable<T> : IEquatable<T> { };

  public static partial class ExtensionMethods {
    public static bool Equals<T>(this T inst, object obj, Func<T, bool> thisEquals) where T : IImmutableEquatable<T> {
      Console.WriteLine($"  Equals Extention Method typeof(T)=={typeof(T).ToString()} , inst.GetType()=={inst.GetType().ToString()} , obj.GetType()=={(obj is null ? "null" : obj.GetType().ToString())}");

      return object.ReferenceEquals(inst, obj) // same reference ->  equal
      || !(obj is null) // this is not null but obj is -> not equal
      && inst.GetType() == obj.GetType() // obj is more derived than this -> not equal
      && inst.GetHashCode() == obj.GetHashCode() // optimization, hash codes are different -> not equal
      && obj is T o // obj cannot be cast to this type -> not equal
      && thisEquals(o);
    }

    public static int GetHashCode<T>(this T inst, ref int? hashCache, Func<int> thisHashCode) where T : IImmutableEquatable<T> {
      Console.WriteLine("  Extention Method GetHashCode " + typeof(T).ToString());
      if (hashCache is null) hashCache = thisHashCode();
      return hashCache.Value;
    }
  }
}


class Base : IImmutableEquatable<Base> {
  public readonly int X;
  readonly int Y;

  public Base(int X, int Y) {
    this.X = X;
    this.Y = Y;
  }

  public bool ThisEquals(Base o) {
    Console.WriteLine("  Base ThisEquals");
    return X.Equals(o.X) && Y.Equals(o.Y);
  }
  public int ThisHashCode() {
    Console.WriteLine("  Base ThisGetHashCode");
    return Tuple.Create(X, Y).GetHashCode();
  }

  // boilerplate
  public override bool Equals(object o) { Console.WriteLine(" Base.Equals(object)"); return this.Equals(o, ThisEquals); }
  public bool Equals(Base o) { Console.WriteLine(" Base.Equals(Base)"); return object.Equals(this, o); }
  public static bool operator ==(Base o1, Base o2) { Console.WriteLine(" Base=="); return object.Equals(o1, o2); }
  public static bool operator !=(Base o1, Base o2) { Console.WriteLine(" Base!="); return !object.Equals(o1, o2); }
  protected int? hashCache;
  public override int GetHashCode() => this.GetHashCode(ref hashCache, ThisHashCode);
}

class Derived : Base, IImmutableEquatable<Derived> {
  public readonly int Z;
  readonly int K;

  public Derived(int X, int Y, int Z, int K) : base(X, Y) {
    this.Z = Z;
    this.K = K;
  }

  public bool ThisEquals(Derived o) {
    Console.WriteLine("  Derived ThisEquals");
    return base.ThisEquals(o) && Z.Equals(o.Z) && K.Equals(o.K);
  }

  public new int ThisHashCode() {
    Console.WriteLine("  Derived ThisGetHashCode");
    return Tuple.Create(base.ThisHashCode(), Z, K).GetHashCode();
  }

  // boilerplate
  public override bool Equals(object o) { Console.WriteLine(" Derived.Equals(object)"); return this.Equals(o, ThisEquals); }
  public bool Equals(Derived o) { Console.WriteLine(" Derived.Equals(Derived)"); return object.Equals(this, o); }
  public override int GetHashCode() => this.GetHashCode(ref hashCache, ThisHashCode);
}

public class Program {
  public static void Main() {
    Derived d1 = new Derived(0, 1, 2, 3), d2 = new Derived(0, 1, 4, 5), d3 = new Derived(0, 1, 2, 3), d4 = null;
    Base b1 = d1, b2 = d2, b3 = d3, b4 = d4, b5 = null;

    Console.WriteLine("d1==d2"); Console.WriteLine("   " + (d1 == d2));
    Console.WriteLine("\nd1.Equals(d2)"); Console.WriteLine("   " + d1.Equals(d2) + "\n");

    Console.WriteLine("\nd1==d3"); Console.WriteLine("   " + (d1 == d3));
    Console.WriteLine("\nd1.Equals(d3)"); Console.WriteLine("   " + d1.Equals(d3) + "\n");

    Console.WriteLine("\nd1==d4"); Console.WriteLine("   " + (d1 == d4));
    Console.WriteLine("\nd1.Equals(d4)"); Console.WriteLine("   " + d1.Equals(d4) + "\n");

    Console.WriteLine("\nb1==b2"); Console.WriteLine("   " + (b1 == b2));
    Console.WriteLine("\nb1.Equals(b2)"); Console.WriteLine("   " + b1.Equals(b2) + "\n");

    Console.WriteLine("\nb1==b3"); Console.WriteLine("   " + (b1 == b3));
    Console.WriteLine("\nb1.Equals(b3)"); Console.WriteLine("   " + b1.Equals(b3) + "\n");

    Console.WriteLine("\nb1==b4"); Console.WriteLine("   " + (b1 == b4));
    Console.WriteLine("\nb1.Equals(b4)"); Console.WriteLine("   " + b1.Equals(b4) + "\n");

    Base b = new Base(0, 1);
    Console.WriteLine("\nb==d1"); Console.WriteLine("   " + (b == b1));
    Console.WriteLine("\nb.Equals(d1)"); Console.WriteLine("   " + b.Equals(d1) + "\n");

    Console.WriteLine("\nb5==d1"); Console.WriteLine("   " + (b5 == d1));
    Console.WriteLine("\nb5==d4"); Console.WriteLine("   " + (b5 == d4));
  }
}
;