This one caught me by surprise (again).
Type equality should be easy, right? I pointed to the rules before. But add implicit conversion magic and you are ready for trouble. Let's visit some pieces of the advice when implementing .Equals():
- "Implementing the Equality Operator (==) on Value Types. ...you should overload the equality operator (==) any time equality is meaningful. [...] Implement the equality operator (==) any time you override the Equals method." from Guidelines for Implementing Equals and the Equality Operator (==)
- "If you are programming in a language that supports operator overloading, and you choose to overload the equality operator (==) for a specified type, that type should override the Equals method. Such implementations of the Equals method should return the same results as the equality operator. Following this guideline will help ensure that class library code using Equals (such as ArrayList and Hashtable) works in a manner that is consistent with the way the equality operator is used by application code." from Implementing the Equals Method
From the guides (very reasonable if you ask my opinion) I had the scenario of a value type in which made sense using value equality, so I jumped to implement the equality (and inequality) operator. As I overrode it, from the second piece of advice, I went on and implemented Equals() (and its close friend GetHashCode()). Well... ReSharper did it for me but they follow the guidelines (here and here)
When unit testing my type (test-last style) I found out that I was violating the "principle of least surprise" big time. I have mentioned it before in one of my rants because I am very fond of it. And if we have to find a culprit, that is implicit conversions. They are "facilities" that can go nasty against you very soon.
And I was getting worried because when testing my lame type to be used by a minority of developers in my company. What would they feel when releasing a major piece of software used by millions of people?
Can the reader tell me what these series of simple comparisons would print?
- 5m == 5
- 5 == 5m
I will spare the copy and paste to a simple Snippet Compiler:
What???? Were they not saying that .Equals() and == should behave in the same manner? Were they joking?
No, but the devil is in the details: equality operator is not implemented in Int32, so the default implementation applies, whereas Decimal has its own implementation of the operator plus bunch of implicit converters.
But I still believe that it is wrong to surprise the developer (as saying that 5.Equals(5m) is different from 5 == 5m) so I went ahead and removed implicit conversion from my type, because I think it was the right thing to do.
What would happen if everyone did the same? And I already know my lousy piece of code it's not the massive framework it is becoming and is free of trade-offs. But... wouldn't it be nice?