By now (almost 20 years since .NET was released) this topic has been discussed a looooooot, but still, sometimes, I feel like it’s forgotten, so there’s probably no harm in talking about it again.
Java makes it a bit simpler in this regard, because it doesn’t allow for operator overload (e.g.
==), so developers know the rule really well:
== compares references,
equals actually compares the objects.
Because .NET allows for operator overload, .NET developers end up using
Equals interchangeably, but it isn’t as straightforward.
A quick example
Let’s create a simple class that overrides the
Equals method, as well as overloads the
Now let’s play around with the available comparisons.
This program console output is the following:
a1.Equals(a2): True object.Equals(a1, a2): True a1 == a2: True
So far so good! The objects are the equal, so every comparison returns
object.Equals ends up also calling the overridden
Equals method, the advantage of using it is to avoid getting null reference exception (or having to check for
Now let’s make an apparently harmless change:
Now, does anything change in the output?
a1.Equals(a2): True object.Equals(a1, a2): True a1 == a2: False
false, not good!
Why is that?
Equals method is a “normal” method defined in
object and overridden in our
A class, so it follows the usual polymorphism rules: what matters is the type of the object, not the type of the variable that references it.
== operator is simply defined to compare
A objects, as you can see by the types of parameters used in its definition above. This means it won’t be called on non
A variables. In this case, it ends up using the default object comparison, which means reference comparison. As the references are not the same, we get
Another twisted example
Now let’s look at a slightly more twisted example, using a built-in type, the
s1 == s2: True
So, what’s different here?
For optimization, .NET (and others) uses something called string interning to keep a single copy of the each distinct value, so even though we are declaring it two times, it ends up keeping a single copy.
Due to string interning, the
== works as desired in this situation, because the reference is actually the same.
We can however break this with a slight tweak:
And now we get:
s1 == s2: False s1.Equals(s2): True
So, even though most of the time, with strings, things will just work, there are situations where it won’t, so better be careful.
Wrapping up, the goal was not to say not to use the
== operator, but keep in mind the pitfalls associated with it.
You’re comparing built-in types or types you control and have the correct reference type? Sure, go ahead. Every other case, better go with
Thanks for stopping by, cyaz!