user1968030
user1968030

Reputation:

Comparing C# object

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

Answers (6)

bto.rdz
bto.rdz

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

Matt Burland
Matt Burland

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

Marshal
Marshal

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

Scott Chamberlain
Scott Chamberlain

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

spaceman
spaceman

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

Wormbo
Wormbo

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

Related Questions