Let's look code IL in the IL DASM
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 54 (0x36)
.maxstack 3
.locals init ([0] string A,
[1] string B,
[2] string one,
[3] string two,
[4] string three,
[5] string C,
[6] string D)
IL_0000: nop
IL_0001: ldstr "123"
IL_0006: stloc.0
IL_0007: ldstr "121"
IL_000c: stloc.1
IL_000d: ldstr "1"
IL_0012: stloc.2
IL_0013: ldstr "2"
IL_0018: stloc.3
IL_0019: ldstr "3"
IL_001e: stloc.s three
IL_0020: ldloc.2
IL_0021: ldloc.3
IL_0022: ldloc.s three
IL_0024: call string [mscorlib]System.String::Concat(string,
string,
string)
IL_0029: stloc.s C
IL_002b: ldloc.2
IL_002c: ldloc.3
IL_002d: ldloc.2
IL_002e: call string [mscorlib]System.String::Concat(string,
string,
string)
IL_0033: stloc.s D
IL_0035: ret
} // end of method Program::Main
As you can see, there are 5 string constants
"123", "121", "1", "2", "3"
and two strings obtained by concatenation. Total 7.
Although A and C (B and D) strings are the same, but they are different instances in memory.
To Arghya C.
If we apply the unsafe code and change the value of the variable A:
Console.WriteLine(A + " " + C);
fixed (char* p = A)
{
p[1] = 'x';
}
Console.WriteLine(A + " " + C);
we get the following output:
123 123
1x3 123
As you can see, only the variable A has changed, but the C variable retained its value. This proves that they are different copies.
However, if we write this:
String A = "1" + "2" + "3";
String C = "123";
аfter executing unsafe code above we get the following:
123 123
1x3 1x3
That is, in this case, the variables A and C keeps a reference to the same instance of the string.
At first I wrote the wrong answer, because I thought the compiler is smart enough to understand at compile-time that String C = one + two + three;
concatenates constants and will create reference on the same string.