4

I'm using Visual Studio's Watch panel to investigate variables as I run my program. In the following code, both test1 and test2 variables are added to the Watch panel. As expected, they both evaluate to true.

object a = "123";
object b = "123";
bool test1 = a == b;

bool test2 = (object)"123" == (object)"123";

However, as seen by the third line, if I manually add the expression for the test2 variable, it evaluates to false.

Why the same line yields different result in the Watch window? Is it some kind of optimization in work here? Under what circumstances can the same string be allocated to different addresses?

gunr2171
  • 16,104
  • 25
  • 61
  • 88
AndrewR
  • 592
  • 2
  • 6
  • 20
  • @AndrewR What's the result of `"123"=="123"` in the watch window? What's the result of `a==b` in the watch window? What's the result of `"123".Equals("123")` in the watch window? Is the value up-to-date or does it show a refresh icon/button? – knittl May 08 '23 at 12:46
  • 3
    The perception that the debugger has a built-in C# compiler is not accurate, it has a dedicated expression parser. Your C# code depends on string literals getting interned. So that Object.operator==() returned true, the object references are the same. That doesn't happen in the debugger. – Hans Passant May 08 '23 at 12:50
  • Check out also [this answer](https://stackoverflow.com/questions/71980721/comparing-two-objects-with-operator/71981008#71981008). – Guru Stron May 08 '23 at 12:52
  • @knittl All true. Expression is not a method call, so it's always up-to-date – AndrewR May 08 '23 at 13:02

1 Answers1

6

There are a few things going on here, the first is that when you use string literals in your C# code, these have to be included in the compiled assembly, so to avoid bloating the assembly, the compiler only outputs each unique string once, and they are automatically "interned" at runtime (see: https://learn.microsoft.com/en-us/dotnet/api/system.string.intern). All uses of a given string literal will therefore be the same string instance.

As a result of the above, all uses of "123" in your code are the same string instance at runtime, which is why both test1 and test2 are true, since your == is performing reference equality, and both sides are the same exact string instance in both cases.

However, when you use the watch window, the debugger creates new string instances for each of the string literals in your expression, so they are not the same instance, and so comparing them with object’s == returns false.

Iridium
  • 23,323
  • 6
  • 52
  • 74
  • So basically the answer is that compiler checks if the constants are equal during the compilation, but not during the execution, and that includes the Watch window – AndrewR May 08 '23 at 13:07
  • 2
    @AndrewR - there is slightly more to it than that since it relies on string "interning" (I've updated my answer to clarify this). You can show this happening by using `string.Intern()`, e.g.: both `(object)string.Intern("123") == (object)a` or even `(object)string.Intern("123") == (object)string.Intern("123")` will be `true` even in the watch window. – Iridium May 08 '23 at 13:34
  • Got it, thank you. Before today I thought that .NET always interns strings when they are created. – AndrewR May 08 '23 at 13:44