Reputation:
Consider this code:
object str1 = "shahrooz";
object str2 = "shahrooz";
object int1 = 1;
object int2 = 1;
Console.WriteLine("str1 == str2 : " + (str1 == str2));
Console.WriteLine("int1 == int2 : " + (int1 == int2));
Console.ReadLine();
When you run this code you will get this result:
str1 == str2 : True
int1 == int2 : False
Both compare are object but why the first comparison returns true but second comparison returns false?
Upvotes: 0
Views: 384
Reputation: 6720
From https://msdn.microsoft.com/en-us/library/53k8ybth.aspx?f=255&MSPPError=-2147217396
For predefined value types, the equality operator (==) returns true if the values of its operands are equal, false otherwise. For reference types other than string, == returns true if its two operands refer to the same object. For the string type, == compares the values of the strings.
First time I misunderstood this quote, what it means actually is that there are 2 different operators in this case, one for object, and one for string.
The object operator compares the instance reference, and string operator compares the value of both strings.
In this case
Console.WriteLine(str1 == str2); // True, becuase of compiler optimizations.
// you can test it with:
Console.WriteLine(object.ReferenceEquals(str1, str2)); // this is true
But if you do
object s1 = "shahrooz";
object s2 = "shah";
s2 += "rooz";
Console.WriteLine(s1 == s2);
//You get false, becuase compiler now can't optimize this.
Said that
int1 == int2 // False, because they reference a different instance
int1 == int1 // True, same instance.
Upvotes: 2
Reputation: 45135
A quick and dirty check of memory addressing helps to shine light on this using this (grabbed from here):
public static IntPtr GetAddress(object o)
{
unsafe
{
TypedReference tr = __makeref(o);
IntPtr ptr = **(IntPtr**)(&tr);
return ptr;
}
}
You can then do this:
Console.WriteLine(GetAddress(str1));
Console.WriteLine(GetAddress(str2));
Console.WriteLine(GetAddress(int1));
Console.WriteLine(GetAddress(int2));
I get:
35839968
35839968
35840128
35840152
What you should see is that the address of str1
and str2
are identical, and the address of int1
and int2
are different. This is because your strings are interned (because they are string literals). A single copy of the string shahrooz
is stored in memory and both str1
and str2
are referring to it. Your int1
and int2
on the other hand are being boxed which is taking the value type int
and placing it inside an object
. Each int
(or any other value type) boxed gets put in its own box with its own address.
Now the default behavior of ==
when given two reference types (like object
) is to just compare the memory address (to see if they refer to the same object), and this is what's happening. In the case of the strings, it's the same object. In the case of the ints, it isn't.
As others have suggested, using Equals
will give the desired result because Object.Equals
is virtual
and can be overridden, hence calling the appropriate int.Equals
when given int
and string.Equals
when given a string.
Upvotes: 4
Reputation: 6661
It is because string
is immutable and they point to same references from string intern pool and its not true for any other value type or reference types as they create new references every time you initialize a new object.
To prove above we check references of two strings initialized separately:
object str1 = "shahrooz";
object str2 = "shahrooz";
Console.WriteLine(object.ReferenceEquals(str1, str2)); //True
Console.WriteLine(object.ReferenceEquals(int1, int2)); //False
Which proves that same strings hold same reference but other objects don't. Its not true for all the reference types but strings are treated as special classes.
On the other hand for any other reference types:
Test a = new Test { Num=1, Str="shahrooz"};
Test b = new Test { Num=1, Str="shahrooz"};
a.Equals(b); //False
object.ReferenceEquals(a,b); //False
But if you do,
Test c = a;
a.Equals(c); //True
object.ReferenceEquals(a,c) //True.
Upvotes: 0
Reputation: 127563
It is because operators can't be virtual
, because of that you are calling the ==
operator on Object
.
The ==
operator for object compares references. For structs (like int
) when cast to a object
it will "box" the struct in to a class reference. The code
object int1 = 1;
object int2 = 1;
causes two boxes to be made so when you call ==
on the box it sees the other side is not the same box so it returns false.
Strings are already reference types so they don't get a extra box made when they are put in to a object
. However the compiler treats them special, in the following code
object str1 = "shahrooz";
object str2 = "shahrooz";
The compiler only makes one shahrooz
string in memory and assigns it to both str1
and str2
. This is why it returns true
when you compare them.
If you did the code
public static void Main()
{
object str1 = "shahrooz";
object str2 = "shah" + Console.ReadLine();
Console.WriteLine(str1 == str2);
Console.ReadLine();
}
and typed in rooz
you would get false
as your result because you now have two different string references even though they have the same string content.
use the method Equals
instead of ==
, that does check if the Equals
method was overloaded on derived classes and will unbox structs to compare them.
Upvotes: 2
Reputation: 1101
Doing (int1 == int2)
compares 2 objects, which are different, due to the CLR's boxing: when you put a ValueType inside a ReferenceType, .NET does boxing for you, so you get 2 different objects, each boxes the 1 value that you assigned.
To make the comparison work, you need to either store the 2 numbers in ints,
or do ((int)int1 == (int)int2)
For info about boxing, see this article:
Boxing and Unboxing (C# Programming Guide)
https://msdn.microsoft.com/en-us/library/yz2be5wk.aspx
Upvotes: 4
Reputation: 4992
The ==
operator performs a reference equality check if both operands are of type object
or no more specific equality operator can be determined based on the compile-time types of the operands.
In your case the compile-time operand types are object
, so reference equality is checked. At the time when you assigned 1
to either variable, the runtime "boxed" the value into an object. As a result you get two objects that contain the integer value one, but they are two separate objects, thus reference equality returns false
.
To perform value equality comparison, use the Equals()
method. It can be overridden in subclasses to perform type-specific value comparison, which I believe is what you want to do. The expression int1.Equals(int2)
should return what you want. Since both variables are nullable, you might want to write it as int1 != null ? int1.Equals(int2) : int2 == null
to prevent throwing NullReferenceException when int1
is null
.
Upvotes: 5