Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Minimal code for equality in C#

In this article, Eric Lippert suggests in point #9 that C# has "too much equality". He points out that there are 9 or 10 different methods or operators that can be overloaded to provide object equality.

My first question is - if the Object.Equals(object) method is overridden, is it possible for the compiler to call any of the other equality operators like ==, !=, <=, etc. without code that expressly performs this operation?

In C++, there is precedent for this behavior. The copy constructor can be called by the compiler in certain places where a temporary variable needs to be generated. I'm at least 95% certain that this can't happen in C#, but it really depends on how the compiler is constructed and maybe edge cases as well.

The second question is - if the compiler will never call any of the equality operators indirectly, then would it be ok for a small, medium, or even large project to specify that only the Object.Equals(object) method and IEquatable be used for equality testing, and IComparable used if the type will be used for sorting or other times when determining the rank of the objects is needed? In other words - is it ok to avoid defining the other equality operators if everyone on the project agrees they won't be used and are therefore unnecessary?

Assume that the code is only to be used within the project and won't be exported for use by third parties.

like image 739
Bob Bryan Avatar asked Dec 24 '22 15:12

Bob Bryan


2 Answers

if the Object.Equals(object) method is overridden, is it possible for the compiler to call any of the other equality operators like ==, !=, <=, etc. without code that expressly performs this operation?

The C# compiler has no idea that Equals and == are semantically the same. If you call Equals then that method will be called.

then would it be ok for a small, medium, or even large project to specify that only the Object.Equals(object) method and IEquatable be used for equality testing, and IComparable used if the type will be used for sorting or other times when determining the rank of the objects is needed? In other words - is it ok to avoid defining the other equality operators if everyone on the project agrees they won't be used and are therefore unnecessary?

The danger you run into here is that you get an == operator defined for you that does reference equality by default. You could easily end up in a situation where an overloaded Equals method does value equality and == does reference equality, and then you accidentally use reference equality on not-reference-equal things that are value-equal. This is an error-prone practice that is hard to spot by human code review.

A couple years ago I worked on a static analysis algorithm to statistically detect this situation, and we found a defect rate of about two instances per million lines of code across all codebases we studied. When considering just codebases which had somewhere overridden Equals, the defect rate was obviously considerably higher!

Moreover, consider the costs vs the risks. If you already have implementations of IComparable then writing all the operators is trivial one-liners that will not have bugs and will never be changed. It's the cheapest code you're ever going to write. If given the choice between the fixed cost of writing and testing a dozen tiny methods vs the unbounded cost of finding and fixing a hard-to-see bug where reference equality is used instead of value equality, I know which one I would pick.

like image 89
Eric Lippert Avatar answered Jan 10 '23 14:01

Eric Lippert


My first question is - if the Object.Equals(object) method is overridden, is it possible for the compiler to call any of the other equality operators like ==, !=, <=, etc. without code that expressly performs this operation?

Not that I know of.

The second question is - if the compiler will never call any of the equality operators indirectly, then would it be ok for a small, medium, or even large project to specify that only the Object.Equals(object) method and IEquatable be used for equality testing, and IComparable used if the type will be used for sorting or other times when determining the rank of the objects is needed? In other words - is it ok to avoid defining the other equality operators if everyone on the project agrees they won't be used and are therefore unnecessary?

Technically, what you are suggesting is possible. But in practice, I wouldn't even trust myself to follow through on the established norm, even if I was the only one working on the project. I would also find myself worrying for every LINQ method call that I make: how does this LINQ method work under the covers? Does it need IEquatable? Does it use IComparable? Am I safe here?

If I were to attempt a similar approach to what you are suggesting, I would probably still have my class implement all the relevant equality interfaces, but have the methods throw a NotSupportedException exception by default. That way, at least I'll feel like a I have a safety net in case I mistakenly use the wrong method, or if I use a library method that relies on a different equality interface.

like image 41
sstan Avatar answered Jan 10 '23 14:01

sstan