Polymorphic Equality

The default generation of Equality members provided by Resharper let you choose three different implementations when overriding the "Equals" method in a class (Alt+Insert -> Equality Members):

The default choice is "Exactly the same type as 'this'" which IS NOT polymorphic. I mean, subtypes of that class will be considered not equal regardless of the values:

equalityGeneration
  1. public override bool Equals(object obj){
  2. if (ReferenceEquals(null, obj)){
  3. return false;
  4. }
  5. if (ReferenceEquals(this, obj)){
  6. return true;
  7. }
  8. if (obj.GetType() != this.GetType()){
  9. return false;
  10. }
  11. return Equals((Base) obj);
  12. }

On line 8 it compares the types which in the case of subtypes will be different thus returning false.
I didn't pay attention to this detail today and for some reason assumed that the comparison was going to work for subtypes. Lesson learned: always pay attention to generated code!

This is the generated code to consider subtypes equal:

  1. public override bool Equals(object obj){
  2. if (ReferenceEquals(null, obj)){
  3. return false;
  4. }
  5. if (ReferenceEquals(this, obj)){
  6. return true;
  7. }
  8. var other = obj as Base;
  9. return other != null && Equals(other);
  10. }

And this is yet another implementation that works:

  1. public override bool Equals(object obj){
  2. if (ReferenceEquals(null, obj)){
  3. return false;
  4. }
  5. if (ReferenceEquals(this, obj)){
  6. return true;
  7. }
  8. if (!(obj is Base){
  9. return false;
  10. }
  11. return Equals(other);
  12. }

The other lesson learned is that overriding the Equals method in the child class when the base class already overrides it, increases the complexity too much. The code is hard to follow and surprising. It increases the coupling between the child class and the parent.
Avoid overriding the equality members in class hierarchies if you can.

Enjoyed reading this post?
Subscribe to the RSS feed and have all new posts delivered straight to you.