In the project that I'm working on, I had the need to overload the Equals operator for a custom class that was being used. This is a fairly well known problem, although if you haven't done it before, there are enough traps to make it challenging. More accurately, it is tricky enough that it is better to borrow already working and optimized code then to create it yourself. So naturally, I turn to Google.
I find a couple of MSDN sites, including this one which describes how to do it. Down towards the bottom of the article, there is a C# example as follows
public static bool operator ==(Complex x, Complex y)
{
return x.re == y.re && x.im == y.im;
}
Perfect. I cut, paste, modify to fit my class, run a couple of basic tests and check the code in.
A few hours later...boom.
All of a sudden some of the other unit tests are failing. Specifically, statements like the following are throwing a NullReferenceException
if (testValue == null)
Hold on a second. This is a test for null. How can it throw an exception.
Here's the catch. The example I took from MSDN assumes that the objects being compared are structs. As such, they can never be null, which is why x.re == y.re can never throw a NullReferenceException. However, if the objects being compared are reference types, then either side can be null. Which means that the sample code would throw an exception. The correct overload (taken from here, which also includes a wonderful description of how to optimize Equals) is as follows.
bool operator ==(SomeType o1, SomeType o2)
{
return Object.Equals(o1, o2);
}
This format not only reuses the logic included in the Equals method, it also has one additional benefit. No NullReferenceExceptions. Unless, of course, your Equals method throws one. But who would make that mistake. ;)
So why am I evil? Well, in the brief period of time that the bad code was checked in, a colleague updated his sandbox, said update including this bug. Three days later, he runs into a NullReferenceException while checking for null. Wastes time scratching his head wondering why a check for null is throwing a null exception. Suggests that the runtime's parents were closely related. Finally, upon consultation with me (because, honestly, who *expects* that the equality operator will be overloaded), realizes that my old code is the source of the problem. Usually my mistakes impact only me. This one, unfortunately, spread its love just a little bit further.