What does IEquatable
As per the documentation IEquality is used to improve performance (it prevents boxing.) Especially usefull in generic collections.
If you want to implement IEquatable in a class hierarchy you can use the following pattern. It prevents derived (including sibling) classes from being equal. If equality is not needed for the derived class you can skip IEquatable but you need to override the CanEqual to prevent it being equal with base classes (unless of course they should be considered equal).
Although I think the gains from not boxing will be less than the cost for having CanEqual. In that case you should seal your types and you no longer need CanEqual. Sealing also has some performance benefits.
public class Base : IEquatable
{
protected virtual bool CanEqual(Base other) => other is Base;
public override bool Equals(object obj) => obj is Base other && Equals(other);
public bool Equals(Base other) => this.CanEqual(other) && other.CanEqual(this) /* && base logic */;
}
public class Derived : Base, IEquatable
{
protected override bool CanEqual(Base other) => other is Derived;
public override bool Equals(object obj) => obj is Derived other && Equals(other);
public bool Equals(Derived other) => this.CanEqual(other) && other.CanEqual(this) /* && derived logic */;
}