C# string reference type?
Solution 1:
The reference to the string is passed by value. There's a big difference between passing a reference by value and passing an object by reference. It's unfortunate that the word "reference" is used in both cases.
If you do pass the string reference by reference, it will work as you expect:
using System;
class Test
{
public static void Main()
{
string test = "before passing";
Console.WriteLine(test);
TestI(ref test);
Console.WriteLine(test);
}
public static void TestI(ref string test)
{
test = "after passing";
}
}
Now you need to distinguish between making changes to the object which a reference refers to, and making a change to a variable (such as a parameter) to let it refer to a different object. We can't make changes to a string because strings are immutable, but we can demonstrate it with a StringBuilder
instead:
using System;
using System.Text;
class Test
{
public static void Main()
{
StringBuilder test = new StringBuilder();
Console.WriteLine(test);
TestI(test);
Console.WriteLine(test);
}
public static void TestI(StringBuilder test)
{
// Note that we're not changing the value
// of the "test" parameter - we're changing
// the data in the object it's referring to
test.Append("changing");
}
}
See my article on parameter passing for more details.
Solution 2:
If we have to answer the question: String is a reference type and it behaves as a reference. We pass a parameter that holds a reference to, not the actual string. The problem is in the function:
public static void TestI(string test)
{
test = "after passing";
}
The parameter test
holds a reference to the string but it is a copy. We have two variables pointing to the string. And because any operations with strings actually create a new object, we make our local copy to point to the new string. But the original test
variable is not changed.
The suggested solutions to put ref
in the function declaration and in the invocation work because we will not pass the value of the test
variable but will pass just a reference to it. Thus any changes inside the function will reflect the original variable.
I want to repeat at the end: String is a reference type but since its immutable the line test = "after passing";
actually creates a new object and our copy of the variable test
is changed to point to the new string.
Solution 3:
As others have stated, the String
type in .NET is immutable and it's reference is passed by value.
In the original code, as soon as this line executes:
test = "after passing";
then test
is no longer referring to the original object. We've created a new String
object and assigned test
to reference that object on the managed heap.
I feel that many people get tripped up here since there's no visible formal constructor to remind them. In this case, it's happening behind the scenes since the String
type has language support in how it is constructed.
Hence, this is why the change to test
is not visible outside the scope of the TestI(string)
method - we've passed the reference by value and now that value has changed! But if the String
reference were passed by reference, then when the reference changed we will see it outside the scope of the TestI(string)
method.
Either the ref or out keyword are needed in this case. I feel the out
keyword might be slightly better suited for this particular situation.
class Program
{
static void Main(string[] args)
{
string test = "before passing";
Console.WriteLine(test);
TestI(out test);
Console.WriteLine(test);
Console.ReadLine();
}
public static void TestI(out string test)
{
test = "after passing";
}
}
Solution 4:
"A picture is worth a thousand words".
I have a simple example here, it's similar to your case.
string s1 = "abc";
string s2 = s1;
s1 = "def";
Console.WriteLine(s2);
// Output: abc
This is what happened:
- Line 1 and 2:
s1
ands2
variables reference to the same"abc"
string object. - Line 3: Because strings are immutable, so the
"abc"
string object do not modify itself (to"def"
), but a new"def"
string object is created instead, and thens1
references to it. - Line 4:
s2
still references to"abc"
string object, so that's the output.
Solution 5:
Actually it would have been the same for any object for that matter i.e. being a reference type and passing by reference are 2 different things in c#.
This would work, but that applies regardless of the type:
public static void TestI(ref string test)
Also about string being a reference type, its also a special one. Its designed to be immutable, so all of its methods won't modify the instance (they return a new one). It also has some extra things in it for performance.