73

See this code:

object x = "mehdi emrani";
string y = "mehdi emrani";
Console.WriteLine(y == x);

that returns true.

But this code:

object x = "mehdi emrani";
string y = "mehdi ";
y += "emrani";
Console.WriteLine(y == x);

returns false.

So when I compare String and Object in first code I get true.
But when I compare them in second code I get false.

Both strings are same but why when I append to the string, my result returns false?

shA.t
  • 16,580
  • 5
  • 54
  • 111
Mehdi Emrani
  • 1,313
  • 2
  • 14
  • 26
  • To compare strings you should always use the `.Equals` function – RononDex Jan 22 '14 at 09:00
  • 3
    @RononDex He say Why?? – Mohammad Jan 22 '14 at 09:02
  • 11
    @RononDex: that's true for java but not for C#, normally you would compare strings, then the `==` operator is overloaded which has the advantage that it handles `null` properly. – Tim Schmelter Jan 22 '14 at 09:10
  • 1
    Read warning messages from the C# compiler carefully. They try to help an guide your. In your case you get ***warning CS0253: Possible unintended reference comparison; to get a value comparison, cast the right hand side to type 'string'***. That should alarm you, and give you the explanation you seek. – Jeppe Stig Nielsen Jan 22 '14 at 15:04

6 Answers6

96

In each case, the second operand of == is x, which is of type object. That means you're using the normal reference equality operator.

Now in your first case, you're using two string constants with the same contents. The C# compiler will use a single object for those two references. In the second case, x and y refer to distinct string objects with the same contents. The two references will be different, so == will return false.

You can fix the comparison by:

  • Use Equals instead - that's overridden by string (as opposed to the == operator which is only overloaded:

    Console.WriteLine(y.Equals(x)); // or x.Equals(y), or Equals(y, x)
    

    The use of the static Equals(object, object) method can be useful if either of the arguments can be null; it means you don't need to worry about a NullReferenceException.

  • Make both variables of type string, at which point the == overload within string will be picked at compile-time, and that overload compares the contents of the strings, not just the references

It's worth noting that it's not just a matter of the string literals itself being noticed by the C# compiler - it's about compile-time constant expressions. So for example:

object x = "mehdi emrani";
string y = "mehdi " + "emrani";
Console.WriteLine(y == x); // True

Here y is initialized using two string literals which aren't the same as the one used to initialize x, but the string concatenation is performed by the compiler, which realizes it's the same string it's already used for x.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • I believe it would be proper to mention the expression "string interning", which is the reason the OP's first case works, even though they are compared as objects because they are the same object. Just to add to the point that the first comparison using the object `==` operator should be avoided. – flindeberg Jan 22 '14 at 10:16
  • 3
    @flindeberg: I avoided using the term interning because that's often considered as an execution-time operation, whereas this is compile-time - the compiler will only emit a single string, so the CLR won't need to intern anything. – Jon Skeet Jan 22 '14 at 10:20
  • also Console.WriteLine( string.Intern(y) == x ); prints true. In the case you already know that x is Interned – Panos Theof Jan 22 '14 at 13:08
  • @PanosTheof Do you mean `Console.WriteLine((object)string.IsInterned(y) == x);` because yes, it writes True. However, `Console.WriteLine((object)string.IsInterned(y) == (object)y);` writes False, in the second case of the original question. They, `x` and `y`, are different instances, one of the two instances is in the string intern pool. – Jeppe Stig Nielsen Jan 22 '14 at 15:17
  • a) Yes it's the same and probably more correct than string.Intern(). b) The second case he compares y with x and not y with y. Also x is of type of object and the cast to (object) is not needed – Panos Theof Jan 22 '14 at 16:13
  • One of these objects is not like the other! – John Odom Jan 22 '14 at 19:46
33

When you initialized

object x = "mehdi emrani";  //pointer(x)

It initialized it in memory and assign reference to x. After this when you initialized

string y = "mehdi emrani"; //pointer(x)

ref

compiler find that this value is already in memory so it assign same reference to y.

Now == equal operator which actually compares the addresses instead of value find the same address for both variable which results true:

x==y  //actually compares pointer(x)==pointer(x) which is true

In second case when you initialized x and y that get assigned different addresses.

object x = "mehdi emrani";  //Pointer(x)
string y = "mehdi ";        //not found in memory
y += "emrani";              //Pointer(y)

Now comparison find different addresses which results false:

x == y  //is actually Pointer(x) == Pointer(y) which is false

So to overcome this you need to use .Equals() which instead of reference compares the value and object type.

Console.WriteLine(y.Equals(x));   //compares "mehdi emrani" == "mehdi emrani" results true
Zaheer Ahmed
  • 28,160
  • 11
  • 74
  • 110
  • Are you sure that if you assign a literal string value to a string it checks to see if there is already a string in memory that is the same and assigns that pointer? If it does I think that is a bad decision. If I were to y += " veci"; that would also change x. I think you mean to say y = x; which would set the pointer – Chad Jan 22 '14 at 15:25
  • @Chad 1) all equal string constants share the same instance since they're interned. This includes constants formed by concatenation. But it doesn't include concatenation happening at runtime. 2) `+=` does not mutate the value on the left side. It creates a new instance and assigns it to the variable. `x += y` is very similar to `x = x + y` in C# (unlike C++ where it can be a distinct mutating operation). – CodesInChaos Jan 22 '14 at 16:40
  • @CodesinChaos - Do you have any reference to back this up and provide further reading? If this is true I think it is something important to understand fully. – Chad Jan 22 '14 at 16:44
7

Most probably the references are compared (standard Equals implementation for object). In the first example C# optimizes constant strings, and so y and x actually point to the same object, hence their reference is equal. In the other case, y is created dynamically, and so the reference is different.

Grzenio
  • 35,875
  • 47
  • 158
  • 240
7

In the first case, .NET performs string constant optimization and allocates only one String instance. Both x and y points to same object (both the references are equal).

But in the second case x and y are pointing to different String instances. Adding "ermani" to y creates a third string object.

"==" operator basically returns true if operands on both sides refer to same object. In the first case x and y refer to same object and in the seconds case x & y refer different objects.

5

In the background a new string is created every time you modify an existing one, because strings are immutable, which means they can't change.

See the following for an explanation: Why .NET String is immutable?

Community
  • 1
  • 1
DaveDev
  • 41,155
  • 72
  • 223
  • 385
5

Have you tried:

Console.WriteLine(y == x.ToString());
Majid
  • 13,853
  • 15
  • 77
  • 113
i486
  • 6,491
  • 4
  • 24
  • 41
  • What would that achieve? The objects are already strings and simply `String.ToString()` returns the original string – Panagiotis Kanavos Jan 22 '14 at 09:05
  • 2
    @PanagiotisKanavos: That would indeed solve the issue since then the compiler knows that `x.ToString` returns a string, hence it uses the overloaded `==` operator of string which compares contents instead of references. However, i'm missing more details in this answer. – Tim Schmelter Jan 22 '14 at 09:16
  • 1
    So would a simple cast or using a string variable in the first place. – Panagiotis Kanavos Jan 22 '14 at 09:20
  • @PanagiotisKanavos: but here it's an object, also, `String.ToString` is a non-op (`return this;`). – Tim Schmelter Jan 22 '14 at 09:24
  • @PanagiotisKanavos The `==` operator is overloaded by the string class, which means statically. So the only the static types of the operands matter, not the runtime types. – flindeberg Jan 22 '14 at 09:59
  • Compile original case and my suggestion and see the differences. "x" is object and without ToString() the comparison is not the expected one. – i486 Jan 30 '14 at 11:25