Hello!
In C# all types can be split into reference and value types. This implies that there is a need for two different types of equality comparison mechanisms. Value and referential equality exist to allow us compare value and reference types. By default, value types use the value equality, and reference types rely on referential equality.
Before I proceed in comparing these equality types, let me try getting your attention with a simple example. Which of the two methods below would you rather have, if you were worried about run-time stability?
public static bool Equals1(double? a, double? b) { if (a == b) return true; else return false; } public static bool Equals2(object a, object b) { if (a.Equals(b)) return true; else return false; }
The acceptable answer is another question – it depends on what you are trying to achieve and the context of the task at hand. However, Equals2 is a small but a potentially dangerous function. If object a is null, we get a Null Reference Exception, as in the following example. The == comparison, on the other hand, will never fail so badly.
The reason for the behavior difference lies in the main difference between how == and object.Equals are implemented internally. The first is a static operator (or a function, if you like), and the second is a virtual method (remember you can override Equals?). Since dLeft is null, it cannot be de-referenced to call an Equals method on. The == operator, being a static function, resolves the type of dLeft at compilation. In our example, it is a boxed double (yes, nullable value types come at a cost). But even though we boxed our doubles, we get value type comparison in Equals1.
So, do we always have to check for null before calling Equals on them? Not always, as we can utilize the static helper object.Equals(object,object) instead. This method accepts two arguments and lets us avoid having to check for equality on a potentially null reference. Here is a much less dangerous Equals2 for you:
public static bool Equals2(object a, object b) { if (object.Equals(a,b)) return true; else return false; }
To continue comparing ‘==’ with Equals, note that the latter is reflexive, meaning that two NaNs are equal according to Equals, and are different according to ‘==’. Other than execution speed, with value types ‘==’ and Equals behave the same. With reference types, the default behavior of Equals is value-type equality and with ‘==’ operator it is referential equality. This can be demonstrated with a simple example:
double left = 10; double right = left; double middle = 10; if (right.Equals(middle)) Console.WriteLine("Same doubles"); else Console.WriteLine("Diff doubles"); if (right==middle) Console.WriteLine("Same doubles"); else Console.WriteLine("Diff doubles"); object oLeft = 10; object oRight = oLeft; object oMiddle = 10; if (oRight.Equals(oMiddle)) Console.WriteLine("Same objects"); else Console.WriteLine("Diff objects"); if (oRight == oMiddle) Console.WriteLine("Same objects"); else Console.WriteLine("Diff objects");
The above will print that two variables are the same in the first 3 cases. In the last case, by default, ‘==’ for reference types performs referential equality comparison. Finally, if you are into numerical comparisons, I would recommend sticking with ‘==’ since it is faster than calling Equals or even CompareTo. For comparing monetary amounts, the difference between two doubles can be compared to some predefined numerical threshold (that is, still using ==).
Happy programming!