6

I know that string is immutable and StringBuilder is mutable. But can anybody explain following code output? Since both are reference types, why do they have different results?

String s1 = "hello";
String s2 = "hello";
Console.WriteLine(s1 == s2); //true
Console.WriteLine(Object.ReferenceEquals(s1, s2)); //true

StringBuilder sb1 = new StringBuilder("hello");
StringBuilder sb2 = new StringBuilder("hello");
Console.WriteLine(sb1 == sb2); //false
Console.WriteLine(Object.ReferenceEquals(sb1,  sb2)); //false
4
  • 1
    You're comparing two different StringBuilder object references. They're two separate objects.
    – wkl
    Commented May 19, 2012 at 2:37
  • +1, not only for an excellent question, but for asking it so clearly, with a complete sample of code that's commented perfectly.
    – Adam Liss
    Commented May 19, 2012 at 2:51
  • 1
    This question actually has nothing to do with StringBuilder. It is really about "why do two string variables initialized with two string literals containing the same character sequence compare as being the same object"? Commented May 19, 2012 at 3:03
  • Use string s2 = "hel" + "lo"; to see that it doesn't have anything to do with types. Commented May 19, 2012 at 3:14

4 Answers 4

10

Since both are reference types, why do they have different results?

Because string objects are highly optimized. In particular, since they're immutable, they can be interned by the compiler to prevent duplication.

If you have two different string objects that both represent the exact same string of characters (as in your example), the compiler will recognize that and maintain only one instance of the actual string object.

The result is that both the s1 and s2 objects actually are the same object as far as the compiler is concerned and even reference the same location in memory.

This bookkeeping happens behind the scenes in something called an "intern table", but that's not really something you need to worry yourself with. The important thing is that all string literals are interned by default by the compiler.

The same kind of thing does not happen for StringBuilder objects, because they are not immutable. They're designed to allow you to modify a string object, and as such, the optimizations don't make much sense. That's why your sb1 and sb2 objects are actually seen as two different objects.

The rule of thumb is quite simple: Use string by default, or when you want a single immutable string of characters. Only use StringBuilder when you want to modify the same string multiple times, say in a loop or other relatively short section of code.

Relevant reading: Optimizing C# String Performance

0
4

When you declare

String s1 = "hello";
String s2 = "hello";

the compiler is smart enough to know that the two strings are (and will always be) identical, so it stores "hello" only once and creates s1 and s2 as aliases for the same physical memory. Later, when you test for equality, the two are equal because they are essentially the same variable.

On the other hand, when you declare

StringBuilder sb1 = new StringBuilder("hello");
StringBuilder sb2 = new StringBuilder("hello");

the compiler creates two variables (because they are both mutable but happen to be initialized to the same value). It copies the string "hello" into each of them, but now there are 2 copies, because each one might later be changed. So even though their contents are the same, they're 2 different entities that reside in different physical memory locations, so the tests for object equality fail.

3

By default, when two reference-type objects are compared, result is true only if both references are equal, that means that both operands must reference the same instance of object. Because objects referenced by sb1 and sb2 are two different objects, result of comparison of StringBuilders is false.

But String class overrides equality operator in a such way, that it doesn't compare objects by their references, but by their values, because this behavior is very intuitive and expected by programmers. That explains why s1 == s2 returns true.

The reason why Object.ReferenceEquals(s1, s2) also returns true (although it seems that s1 and s2 references different instances of string) is called string interning. It causes that CLR puts all occurrences of identical string literals (such as "hallo" and "hallo" in your example) to internal pool of strings only once per application, so both s1 and s2 actually references the same instance of string "hallo". This is possible, because string are immutable.

1

When you are doing the following you are comparing two different StringBuilder objects not their values:

StringBuilder sb1 = new StringBuilder("hello");
StringBuilder sb2 = new StringBuilder("hello");
Console.WriteLine(sb1 == sb2); //false

This is how you can compare their values:

Console.WriteLine(sb1.ToString() == sb2.ToString());

Not the answer you're looking for? Browse other questions tagged or ask your own question.