3

Consider this code:

int a = 0;
short b = 0;
int c = 0;
object a1 = a;
object b1 = b;
object c1 = c;

Console.WriteLine(1);
//comparing primitives - int vs. short
Console.WriteLine(a == b);
Console.WriteLine(b == a);
Console.WriteLine(a.Equals(b));
Console.WriteLine(b.Equals(a));

Console.WriteLine(2);
//comparing objects - int vs. int
Console.WriteLine(c1 == a1);
Console.WriteLine(a1 == c1);
Console.WriteLine(c1.Equals(a1));
Console.WriteLine(a1.Equals(c1));

Console.WriteLine(3);
//comparing objects - int vs. short
Console.WriteLine(a1 == b1);
Console.WriteLine(b1 == a1);
Console.WriteLine(a1.Equals(b1)); //???
Console.WriteLine(b1.Equals(a1));

It prints this output:

1
True
True
True
False
2
False
False
True
True
3
False
False
False
False

What I know; what is clear

Section 2: == operator returns true when used with objects only if it compares one object in memory referenced by two different names (not very frequent, but might happen). Equals() method compare content (value) of the objects. It is mentioned in many answers on this site.

Section 1: Using == operator, compiler converts ‘smaller’ type to ‘bigger’ (short to int) and compares primitive values. Order of operands (variables) doesn’t matter. Result of Equals() in the last line might be confusing, because it returns false (doesn’t compare values), but it is understood. The order matter here. As learned in this answer, the best overload must be selected. It is selected by the type of the first variable: short.Equals(short). But then int cannot be converted to ‘smaller’ type (short), therefore there is no comparison made and method returns false.

Questions:

  1. Is my understanding above correct?
  2. Why the last two lines of section 3 (using Equals()) both return false? Why is there difference to section 1 line 3? Why isn’t the overload and the value comparison made? It is getting quite abstract and I can’t find the reason.
Cœur
  • 37,241
  • 25
  • 195
  • 267
jcjr
  • 1,503
  • 24
  • 40
  • 2
    They return false because you have [boxed](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/types/boxing-and-unboxing) the value-type `int` in an `Object`. This is not the same instance(`Object.Equals` uses `Object.ReferenceEquals`) so `Equals` returns false. When a value type is boxed, a new object is allocated and constructed. – Tim Schmelter Oct 19 '17 at 11:59
  • @TimSchmelter note that `(b.Equals(a));` will give you false and there is no boxing – BRAHIM Kamel Oct 19 '17 at 12:13
  • @BRAHIMKamel: i have answered OP's seond question – Tim Schmelter Oct 19 '17 at 12:23
  • @TimSchmelter I think it's not true, because `object.Equals` is virtual method so in OP case `int.Equals` will be called, not `object.Equals`. – Evk Oct 19 '17 at 12:25
  • @BRAHIMKamel: the reason why `b.Equals(a)` returns `false` is that [`int16.Equals`](https://msdn.microsoft.com/en-us/library/de7hf7c3(v=vs.110).aspx) calls `Equals(Object)` if the type is not implicitely convertible to `short`. `Int32` is not, so it returns false. If you reverse the condition you get `true` because a `short` has an implicit conversion to `int`. Read: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/implicit-numeric-conversions-table – Tim Schmelter Oct 19 '17 at 12:28
  • @TimSchmelter thanks I know but take a look at my answer :) – BRAHIM Kamel Oct 19 '17 at 12:29
  • @TimSchmelter if you box two ints with the same value and then use Equals to compare them - you will see it returns true. You can also create some struct, override Equals, box it and you will see its Equals will be called, not Object.Equals. – Evk Oct 19 '17 at 12:32

2 Answers2

2

In section 1 line 3

int a = 0;
short b = 0;
Console.WriteLine(a.Equals(b));

you call this overload of int: bool Equals(int other), because b (short) can be implicitly converted to int so this overload is chosen. It returns true. In Section 3 line 3

int a = 0;
short b = 0;
object a1 = a;
object b1 = b;
Console.WriteLine(a1.Equals(b1)); //???

another overload of int (not object, because Equals is virtual method) is called: bool Equals(object other). For it to return true other should have exactly the same type (int), but it's really short so it returns false. Boxing is not relevant here, which you can verify with this:

int a = 0;            
int c = 0;
object a1 = a;
object c1 = c;
// yes, different objects
Console.WriteLine(a1 == c1); // false
// still equal, because both are boxed ints
Console.WriteLine(a1.Equals(c1)); // true

As for understanding, I think documentation contains all relevant information. Just remember that:

  1. Both == operator and Equals method can be manually defined in class and so in theory can do anything. Your understanding relates only to "default" behavior.

  2. == is not virtual in common sense, unlike Equals method. So when you do a1 == b1 - == to be called in defined in compile time (based on types of a1 and b1), but when you call a1.Equals(b1) - it is virtually dispatched so method to call is defined at runtime.

Evk
  • 98,527
  • 8
  • 141
  • 191
1

In addition to boxing which means that a value type will result in a different reference in memory you have to consider Implicit Numeric Conversions in fact, for this reason, you have

Console.WriteLine(a.Equals(b)); 

which gives you a true but not this

Console.WriteLine(b.Equals(a));

here another example

static void Main(string[] args)
        {


            int i = 0;
            long L = 0;


            Console.WriteLine(1);
            //comparing primitives - int vs. short

            Console.WriteLine(L.Equals(i)); //true
            Console.WriteLine(i.Equals(L));//false
        }
BRAHIM Kamel
  • 13,492
  • 1
  • 36
  • 47