I was asked this question in an interview: Is string a reference type or a value type.
I said its a reference type. Then he asked me why don\'t we use new operator
Strings are immutable reference types. There's the ldstr IL instruction which allows pushing a new object reference to a string literal. So when you write:
string a = "abc";
The compiler tests if the "abc"
literal has already been defined in the metadata and if not declare it. Then it translates this code into the following IL instruction:
ldstr "abc"
Which basically makes the a
local variable point to the string literal defined in the metadata.
So I would say that your answer is not quite right as the compiler doesn't translate this into a call to a constructor.
Well, it's correct that the compiler has special syntax that simplifies the creation of strings.
The part about the compiler producing a call to the constructor is not really correct. String literals are created when the application starts, so where the string literal is used it's only an assignment of a reference to an already existing object.
If you assign a string literal in a loop:
string[] items = new string[10];
for (int i = 0; i < 10; i++) {
items[i] = "test";
}
it will not create a new string object for each iteration, it will just copy the same reference into each item.
Two other noteworthy things about string literals is that the compiler doesn't create duplicates, and it automatically combines them if you concatenate them. If you use the same literal string more than once, it will use the same object:
string a = "test";
string b = "test";
string c = "te" + "st";
The variables a
, b
and c
all point to the same object.
The string class also has constructors that you can use:
string[] items = new string[10];
for (int i = 0; i < 10; i++) {
items[i] = new String('*', 42);
}
In this case you will actually get ten separate string objects.
As everyone said, string is immutable, so there is no constructor call implicitly. I would like to add the following reference for you, which may clear the air a bit more :
String Immutability
Nope. The compiler does not change the construction. What type should the constructor argument be? String? ;-)
String literals are constants without a name.
In addition, you can initialize any class with a string literal, if it supports an operator:
public class UnitTest1 {
class MyStringable {
public static implicit operator MyStringable(string value) {
return new MyStringable();
}
}
[TestMethod]
public void MyTestMethod() {
MyStringable foo = "abc";
}
}
Taken this test method:
[TestClass]
class MyClass {
[TestMethod]
public void MyTest() {
string myString = "foo";
if (myString == "bar")
Console.WriteLine("w00t");
}
}
Creates the following IL code:
.method public hidebysig instance void MyTest() cil managed
{
.custom instance void [Microsoft.VisualStudio.QualityTools.UnitTestFramework]Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute::.ctor()
.maxstack 2
.locals init (
[0] string myString,
[1] bool CS$4$0000)
L_0000: nop
L_0001: ldstr "foo"
L_0006: stloc.0
L_0007: ldloc.0
L_0008: ldstr "bar"
L_000d: call bool [mscorlib]System.String::op_Equality(string, string)
L_0012: ldc.i4.0
L_0013: ceq
L_0015: stloc.1
L_0016: ldloc.1
L_0017: brtrue.s L_0024
L_0019: ldstr "w00t"
L_001e: call void [mscorlib]System.Console::WriteLine(string)
L_0023: nop
L_0024: ret
}
As you see, all string values (foo, bar and w00t) are still strings and do not call any hidden constructor.
Hope this is more explaining.
But we can use new operator while to initialize the string
String str = new char[] {'s','t','r'};
Is this answer correct or not ?
Not, the string are cached and used, let say as is in the IL.
Not exactly the right answer. Strings are "special" reference types. They are immutable. You are right that compiler does something internally, but it is not the constructor call. It calls ldstr which pushes a new object reference to a string literal stored in the metadata.
Sample C# code :
class Program
{
static void Main()
{
string str;
string initStr = "test";
}
}
and here is the IL code
.method private hidebysig static void Main() cil managed
{
.entrypoint
// Code size 8 (0x8)
.maxstack 1
.locals init ([0] string str,
[1] string initStr)
IL_0000: nop
IL_0001: ldstr "test"
IL_0006: stloc.1
IL_0007: ret
} // end of method Program::Main
You can see ldstr
call above.
Even more due to immutability of the Strings it becomes possible to keep only distinct/unique strings. All strings are kept in the hash table where the key is the string value and the value is the reference to that string. Each time when we have a new string CLR checks is there already such a string in the hash table. If there is then no new memory is allocated and the reference is set to this existing string.
You can run this code to check :
class Program
{
static void Main()
{
string someString = "abc";
string otherString = "efg";
// will retun false
Console.WriteLine(Object.ReferenceEquals(someString, otherString));
someString = "efg";
// will return true
Console.WriteLine(Object.ReferenceEquals(someString, otherString));
}
}