What is the difference between == and Equals() for primitives in C#?

Mohammad Zargarani picture Mohammad Zargarani · Jan 22, 2014 · Viewed 16.4k times · Source

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?

Answer

SLaks picture SLaks · Jan 22, 2014

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.