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 ints (or shorts or longs).
When you call it with an int and a short, the compiler will implicitly convert the short to int and compare the resulting ints 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 ints 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:

enter image description here 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