If you are compiling them in your code.. they are compile time constants.. i.e. code explicitly references them from your compiled binary, which is of course loaded in memory at runtime..
If you construct them at runtime.. like from char array, I would guess that CLR has the necessary implementation for doing so. For example - look at following code from http://referencesource.microsoft.com/#mscorlib/system/string.cs,97ccd50b20126543
[System.Security.SecuritySafeCritical] // auto-generated
private static String ConcatArray(String[] values, int totalLength) {
String result = FastAllocateString(totalLength);
int currPos=0;
for (int i=0; i<values.Length; i++) {
Contract.Assert((currPos <= totalLength - values[i].Length),
"[String.ConcatArray](currPos <= totalLength - values[i].Length)");
FillStringChecked(result, currPos, values[i]);
currPos+=values[i].Length;
}
return result;
}
[System.Security.SecurityCritical] // auto-generated
[ResourceExposure(ResourceScope.None)]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal extern static String FastAllocateString(int length);
Essentially strings get special treatment in the language, and although they are objects (immutable), you are right in your understanding that they are not instantiated with new
operator in traditional sense. i.e. you don't do what you said in your comment String s=new String("Hello World");
because if you think about it, the new
is redundant, because you have already defined your string in double quotes as a string literal.
And hence while you could use implicit operator, to convert a string to a given type.. It is not the same trick. The trick in case of strings is the native support from CLR.
EDIT:
Here is another proof.. There is a special IL OpCode to load strings.
OpCodes.Ldstr
"Pushes a new object reference to a string literal stored in the metadata."
If you'll look at your compiled code, you'd see ldstr
opcode being used to load strings, instead of newobj
opcode.