What is the difference between == and Equals() for primitives in C#?
Consider this code:
int age = 25;
short newAge = 25;
Console.WriteLine(age == newAge); //true
Console.WriteLine(newAge.Equals(age)); //false
Console.ReadLine();
Both int
and short
are primitive types, but a comparison with ==
returns true and a comparison with Equals
returns false.
Why?
Short answer:
Equality is complicated.
Detailed answer:
Primitives types override the base object.Equals(object)
and return true if the boxed object
is of the same type and value. (Note that it will also work for nullable types; non-null nullable types always box to an instance of the underlying type.)
Since newAge
is a short
, its Equals(object)
method only returns true if you pass a boxed short with the same value. You're passing a boxed int
, so it returns false.
By contrast, the ==
operator is defined as taking two int
s (or short
s or long
s).
When you call it with an int
and a short
, the compiler will implicitly convert the short
to int
and compare the resulting int
s by value.
Other ways to make it work
Primitive types also have their own Equals()
method that accepts the same type.
If you write age.Equals(newAge)
, the compiler will select int.Equals(int)
as the best overload and implicitly convert short
to int
. It will then return true
, since this method simply compares the int
s directly.
short
also has a short.Equals(short)
method, but int
cannot be implicitly converted to short
, so you aren't calling it.
You could force it to call this method with a cast:
Console.WriteLine(newAge.Equals((short)age)); // true
This will call short.Equals(short)
directly, without boxing. If age
is larger than 32767, it will throw an overflow exception.
You could also call the short.Equals(object)
overload, but explicitly pass a boxed object so that it gets the same type:
Console.WriteLine(newAge.Equals((object)(short)age)); // true
Like the previous alternative, this will throw an overflow if it doesn't fit in a short
.
Unlike the previous solution, it will box the short
into an object, wasting time and memory.
Source Code:
Here are both Equals()
methods from the actual source code:
public override bool Equals(Object obj) {
if (!(obj is Int16)) {
return false;
}
return m_value == ((Int16)obj).m_value;
}
public bool Equals(Int16 obj)
{
return m_value == obj;
}
Further Reading:
See Eric Lippert.
Because there is no overload for short.Equals
that accepts an int
. Therefore, this is called:
public override bool Equals(object obj)
{
return obj is short && this == (short)obj;
}
obj
is not a short
.. therefore, it is false.
When you pass int
to short
's Equals you pass object
:
So this pseudocode runs:
return obj is short && this == (short)obj;
For value types, .Equals
requires the two objects to be of the same type and have the same value, while ==
just tests if the two values are the same.
Object.Equals
http://msdn.microsoft.com/en-us/library/bsc2ak47(v=vs.110).aspx