Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Int, Char, Object Datatypes using [==] operator & .Equals()

I'm confused on this code what is the difference between the two conditions? Why the results are not the same?

Example Number 1 - Don't have the same datatype w/ same value, but it returns the same result of true

 int value1 = 'a';
 char value2 ='a'; 
 Console.WriteLine(value1 == value2);
 Console.WriteLine(value1.Equals(value2));



Example Number 2 - They have the same datatype w/ same value, but it returns false & true

object obj1 = "Object One";
object obj2 = new string("Object One".ToCharArray());
Console.WriteLine(obj1 == obj2);
Console.WriteLine(obj1.Equals(obj2));
like image 492
davinceleecode Avatar asked Jun 26 '26 11:06

davinceleecode


2 Answers

There's actually a lot going on in the seemingly simple code in your question so let's deal with it one step at a time. Please note that there is so much going on that I might miss something, use the comment field.

First piece of code first:

int value1 = 'a';
char value2 ='a'; 
Console.WriteLine(value1 == value2);
Console.WriteLine(value1.Equals(value2));

This line:

int value1 = 'a';

should give you a hint about what the reason for the behavior of this code is. There is a silent conversion from a char to an int. In fact, what is compiled doesn't mention a char at all for this variable, it's a number. The number assigned to the int variable is the codepoint value of a, which is 97.

The first comparison:

value1 == value2

is done using the == operator on int, so in reality, a pure numeric comparison is done, as though the character is a number. The same silent conversion takes place here, the character is converted to a number. Since it is the same character and the same conversion, it stands to reason that you end up with 97 from this comparison as well.

This is mentioned in the spec under section 6.1.2, Implicit numeric conversions:

The implicit numeric conversions are:
...
From char to ushort, int, uint, long, ulong, float, double, or decimal

That means that what is written is actually equivalent to:

97 == 97

The second comparison:

value1.Equals(value2)

is done using the exact same conversion, so you have:

97.Equals(97)

So let's make the first piece of code super-clear by adding explicit casts and changing the code to be what the compiler sees:

// int value1 = (int)'a';                        // 97
int value1 = 97;
char value2 = 'a';
Console.WriteLine(value1 == (int)value);         // 97 == 97
Console.WriteLine(value1.Equals((int)value2));   // 97.Equals(97);

I also asked LINQPad to show me the decompilation of these two statements:

int a = 97;
int a = 'a';

And they both compile to:

ldc.i4.s 61             // 0x61 = 97

So just to be clear, this silent conversion is done by the compiler, there is no runtime code that converts the character to an int for the declaration, the code executes and is compiled as though you had actually written

int value1 = 97;

So that is the reason for this part.

Now on to the next part:

object obj1 = "Object One";
object obj2 = new string("Object One".ToCharArray());
Console.WriteLine(obj1 == obj2);
Console.WriteLine(obj1.Equals(obj2));

Here you are first declaring two object variables, this is important, and then give them the same string value, although they are two distinct instances.

So let's deal with the first comparison:

obj1 == obj2

This is done using the == operator defined for object, which compares references. Since we've already established that your funky string construction for the second variable constructs a new instance, the reference comparison indicates that they are different.

The reason why it is is using the == operator defined on object, and not the on defined on string is because operators are resolved at compile-time and at compile-time the compiler only knows that the variables are of type object. The fact that they contain strings, even the fact that the compiler could "see" that you've just assigned strings to them, so that it should use the string == operator instead, is ignored.

However, when you do this:

obj1.Equals(obj2)

Then you are calling the virtual .Equal(object other) declared in object, which is overridden in string, and thus you get string content comparison, which indicates they are the same.

So let's make the second piece of code super-clear as well:

object obj1 = "Object One";
object obj2 = new string("Object One".ToCharArray());
Console.WriteLine(obj1.ReferenceEquals(obj2));          // <-- changed
Console.WriteLine(obj1.Equals(obj2));
like image 156
Lasse V. Karlsen Avatar answered Jun 28 '26 00:06

Lasse V. Karlsen


In Example 1, int and char are value types so the equality comparison (via == or Equals) defaults to bit by bit comparison (as documented here) as none of them overrides == and their Equals implementation delegates the work to ==.

true is returned in both cases because value1 and value2 have the same binary representation.

In Example 2, string is a reference type, obj1 and obj2 are references to 2 string instances which happen to have the same content.

But since they are declared as object, and == is resolved at compile time, the only option for the compiler is to emit a reference comparison (=false)

In contrast, obj1.Equals will actually be a call to string.Equals(object) which will return the comparison of the string values

like image 21
vc 74 Avatar answered Jun 28 '26 02:06

vc 74