Thursday, July 07, 2005

Is System.String a value type or a reference type?

First, what is the difference between a value type and a reference type?
A value type stores its data. When a value type is passed around, its data is copied and passed. This means that manipulating a copy of a value type, does nothing to the object that was copied.
int i = 100;int j = i;j = 200;
at this point i = 100 and j = 200. This is the behavior of value types.
A reference type on the other hand stores only a reference to the data that it stores. This means that a copy of a reference type is just an alias to the original object. Classes are an example of a reference type. Lets look at an example.
public class refType{ public string s = ""; public refType() { }}public class MyClass{ public static void Main() { refType r1 = new refType(); r1.s = "refType r1"; refType r2 = r1; r2.s = "refType r2"; Console.WriteLine("R1 " + r1.s); Console.WriteLine("R2 " + r2.s); }}
The output of the above code will be
R1 refType2R2 refType2
This is because by writing r2 = r1, we have made r2 into an alias to r1, as r1 and r2 reference the same data. Thats the reason that they are called reference types.
Now, about the topic: is System.String a reference type or a value type?
Lets look at some code first:
public class refType{ public string s = ""; public refType() { }}public class MyClass{ public static void Main() { int i = 100; int j = i; j = 200; Console.WriteLine("i " + i.ToString()); Console.WriteLine("j " + j.ToString()); refType r1 = new refType(); r1.s = "refType r1"; refType r2 = r1; r2.s = "refType r2"; Console.WriteLine("R1 " + r1.s); Console.WriteLine("R2 " + r2.s); string s1 = "empty"; string s2 = s1; s2 = "hello"; Console.WriteLine("S1 " + s1); Console.WriteLine("S2 " + s2); }}
The output of the above code will be:
i 100
j 200 (which is what we would expect of a value type)
R1 refType r2
R2 refType r2 (which is what we would expect of a class, a reference type)
S1 empty
S2 hello (does this mean string is a value type?)
As the output shows: S2 is not an alias to S1, so strings must not be a reference type and hence must be a value type. But that is not true! If you look at the .NET documentation, System.String is a reference type. Then why is s2 not an alias of s1, just like r2 is an alias of r1. (ie, why does s1 not change when s2 changes, whereas r1 changes when r2 is assigned a different value?)
The reason is that the assignment operator for a string is overloaded. It actually creates a new instance of the string object.
So s1 = "new string" is actually s1 = new string(). When s2 is assigned s1, .NET creates a new string object and copies the data from s1 into s2. This is the reason that s2 is not an alias to s1, because .NET behind the scenes created a new string object for you.
System.String behaves like a value type, even when its passed as a parameter to a method. This is because a copy of it is made and sent to the method. So changing its value within the method, will not get reflected outside the scope of the method. (unless the parameter was sent in with the ref keyword).
This demonstrated by the following code:
public class refType{ public string s = "empty"; public refType() { }}public class MyClass{ public static void Main() { int i = 0; Console.WriteLine("Before " + i.ToString()); intFunc(i); Console.WriteLine("After " + i.ToString()); Console.WriteLine(); refType r1 = new refType(); Console.WriteLine("Before " + r1.s); refFunc(r1); Console.WriteLine("Main " + r1.s); string s1 = "hello world"; Console.WriteLine("Before " + s1); StringFunc(s1); Console.WriteLine("After " + s1); Console.WriteLine(); } static void StringFunc( string s ) { s = "Changed the data"; Console.WriteLine("StringFunc " + s); } static void intFunc( int s ) { s = 1000; Console.WriteLine("intFunc " + s.ToString()); } static void refFunc( refType s ) { s.s = "refFuncSaysHello"; Console.WriteLine("refFunc " + s.s); }}
The output of which would be :
Before 0intFunc 1000After 0 (as expected of a value type)Before emptyrefFunc refFuncSaysHelloMain refFuncSaysHello (as expected of a reference type)Before hello world StringFunc Changed the data After hello world (string a reference type, behaves like a value type)