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.