Why is (object)0 == (object)0 different from ((object)0).Equals((object)0)?

C#.Net

C# Problem Overview


Why are the following expressions different?

[1]  (object)0 == (object)0 //false
[2]  ((object)0).Equals((object)0) // true

Actually, I can totally understand [1] because probably the .NET runtime will box the integer and start comparing the references instead. But why is [2] different?

C# Solutions


Solution 1 - C#

The reason the calls behave different is they bind to very different methods.

The == case will bind to the static reference equality operator. There are 2 independent boxed int values created hence they are not the same reference.

In the second case you bind to the instance method Object.Equals. This is a virtual method which will filter down to Int32.Equals and this checks for a boxed integer. Both integer values are 0 hence they are equal

Solution 2 - C#

When you cast the int value 0 (or any other value type) to object, the value is boxed. Each cast to object produces a different box (i.e. a different object instance). The == operator for the object type performs a reference comparison, so it returns false since the left-hand side and right-hand side are not the same instance.

On the other hand, when you use Equals, which is a virtual method, it uses the implementation of the actual boxed type, i.e. Int32.Equals, which returns true since both objects have the same value.

Solution 3 - C#

The == operator, being static, is not virtual. It will run the exact code that the object class defines (`object being the compile time type of the operands), which will do a reference comparison, regardless of the runtime type of either object.

The Equals method is a virtual instance method. It will be running the code defined in the actual run-time type of the (first) object, not the code in the object class. In this case, the object is an int, so it will perform a value comparison, as that is what the int type defines for its Equals method.

Solution 4 - C#

The Equals() method is virtual.
Therefore, it always calls the concrete implementation, even when the callsite is casted to object. int overrides Equals() to compare by value, so you get value comparison.

Solution 5 - C#

== Use: Object.ReferenceEquals

Object.Equals compares the value.

The object.ReferenceEquals method compares references. When you allocate an object, you receive a reference containing a value indicating its memory location in addition to the object's data on the memory heap.

The object.Equals method compares the contents of objects. It first checks whether the references are equal, as does object.ReferenceEquals. But then it calls into derived Equals methods to test equality further. See this:

   System.Object a = new System.Object();
System.Object b = a;
System.Object.ReferenceEquals(a, b);  //returns true

Solution 6 - C#

The C# operator uses the token == to represent two different operators: a statically- overloadable comparison operator and a non-overloadable reference-comparison operator. When it encounters the == token, it first checks to see if there exists any equality-test overload which is applicable to the operand types. If so, it will invoke that overload. Otherwise, it will check whether the types are applicable to the reference-comparison operator. If so, it will use that operator. If neither operator is applicable to the operand types, compilation will fail.

The code (Object)0 doesn't merely upcast an Int32 to Object: Int32, like all value types, actually represents two types, one of which describes values and storage locations (such as the literal zero), but doesn't derive from anything, and one of which describes heap objects and derives from Object; because only the latter type may be upcast to Object, the compiler must create a new heap object of that latter type. Each invocation of (Object)0 creates a new heap object, so the two operands to == are different objects, each of which, independently, encapsulates the Int32 value 0.

The class Object does not have any usable overloads defined for the equals operator. Consequently, the compiler will not be able to use the overloaded equality-test operator, and will fall back to using the reference-equality test. Because the two operands to == refer to distinct objects, it will report false. The second comparison succeeds because it asks one heap-object instance of Int32 whether it's equal to the other. Because that instance knows what it means to be equal to another distinct instance, it can answer true.

Solution 7 - C#

Both checks are different. The first one checks for identity, the second one for equality. In general two terms are identical, if they are refering to the same object. This implies that they are equal. Two terms are equal, if their values is the same.

In terms of programming identity is usually messured by reference equality. If the pointer to both terms are equal (!), the object they are pointing at is exactly the same one. However, if the pointers are different, the value of the objects they are pointing at can still be equal. In C# identity can be checked using the static Object.ReferenceEquals member, whilst equality is checked using the non-static Object.Equals member. Since you are casting two integers to objects (which is called "boxing", btw), the operatior == of object performs the first check, which is by default mapped to Object.ReferenceEquals and checks for identity. If you explicity call the non-static Equals-member, dynamic dispatch results in a call to Int32.Equals, which checks for equality.

Both concepts are similar, but not the same. They may seem confusing for first, but the small difference is very important! Imagine two persons, namely "Alice" and "Bob". They both are living in a yellow house. Based on the assumption, that Alice and Bob are living in a district, where houses are only differ in their color, they could both live in different yellow houses. If you compare both homes you will recognize, that they are absolutely the same, because they are both yellow! However, they are not sharing the same home and thus their houses are equal, but not identical. Identity would imply that they are living in the same house.

Note: some languages are defining the === operator to check for identity.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAndre PenaView Question on Stackoverflow
Solution 1 - C#JaredParView Answer on Stackoverflow
Solution 2 - C#Thomas LevesqueView Answer on Stackoverflow
Solution 3 - C#ServyView Answer on Stackoverflow
Solution 4 - C#SLaksView Answer on Stackoverflow
Solution 5 - C#user1968030View Answer on Stackoverflow
Solution 6 - C#supercatView Answer on Stackoverflow
Solution 7 - C#CarstenView Answer on Stackoverflow