Custom implementation is usually only necessary if default, reference equality is not enough. In example:
- entities value based equality
 - value equality of deserialized objects
 - custom comparisons based on other criterias
 
public class ImaginaryNumber : IEquatable<ImaginaryNumber>
{
    public double RealNumber { get; set; }
    public double ImaginaryUnit { get; set; }
    public override bool Equals(object obj)
    {
        return Equals(obj as ImaginaryNumber);
    }
    public bool Equals(ImaginaryNumber other)
    {
        return other != null &&
               RealNumber == other.RealNumber &&
               ImaginaryUnit == other.ImaginaryUnit;
    }
    public override int GetHashCode()
    {
        return HashCode.Combine(RealNumber, ImaginaryUnit);
    }
}
	
