Though string variables come under the category of "reference types", they behave more or less the same as "value types".
For example if a class object is "passed by value", we can still change members' values of the class object and that change actually reflects even when the scope of the called method finishes, because a copy of the address gets passed and not the copy of the members' data.
Whereas even though string variables falls under "reference types" and when they're "passed by value", any change done in the called method scope doesn't reflect outside of it, which means even though string variables are categorized as "reference types", the copy of address is not passed rather the copy of data itself is passed, which is why the change reflects only on the copy and it reflects in the called method scope only.
I also ran following example in .NET Editor given at msdn:
string name1 = "Scot";
string name2 = "Garry";
// Passing by value.
// The value of name1 in Main is not changed.
//whereas the value of the member of class object would have changed even when "passed by value" but in string variables it doesn't happen the same.
changeName(name1);
Console.WriteLine(name1);
//Output: Scot
// Passing by reference.
// The value of name2 in Main is changed.
changeNameByRef(ref name2);
Console.WriteLine(name2);
//Output: GarryToRichard
//Passing by value
void changeName(string inputVal)
{
inputVal = "ScotToHarry";
}
//Passing by Reference
void changeNameByRef(ref string inputVal)
{
inputVal = "GarryToRichard";
}
Few questions arise:
⚠Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.
First, let me add the code in a proper formatting:
string name1 = "Scot";
string name2 = "Garry";
// Passing by value.
// The value of name1 in Main is not changed.
//whereas the value of the member of class object would have changed even when "passed by value" but in string variables it doesn't happen the same.
changeName(name1);
Console.WriteLine(name1);
//Output: Scot
// Passing by reference.
// The value of name2 in Main is changed.
changeNameByRef(ref name2);
Console.WriteLine(name2);
//Output: GarryToRichard
//Passing by value
void changeName(string inputVal)
{
inputVal = "ScotToHarry";
}
//Passing by Reference
void changeNameByRef(ref string inputVal)
{
inputVal = "GarryToRichard";
}
Back to your problem, you're expecting the following function to reflect in the main method:
//Passing by value
void changeName(string inputVal)
{
inputVal = "ScotToHarry";
}
what happens here is that you're NOT modifying the string, you're creating a NEW one. That's the reason the change doesn't get reflected in the main.
You can find a good explanation in more details than mine in this article
Ok, I got your point, this refers to the immutable nature of strings, which is explained at the following link:
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/reference-types
Strings are immutable--the contents of a string object cannot be changed after the object is created, although the syntax makes it appear as if you can do this. For example, when you write this code (as can be seen below this para), the compiler actually creates a new string object to hold the new sequence of characters, and that new object is assigned to b. The memory that had been allocated for b (when it contained the string "h") is then eligible for garbage collection.
string b = "h";
b += "ello";
So if I'm understanding it right ! You mean, when we pass string variable as value, the address copy gets passed to the called method, and when the value of the string variable is changed in the called method, it is actually equivalent of creating a new object (of course new address) and assigning it to the string variable, which behaves exactly how it's designed to behave, that means, when a copy of new address is assigned, the effect remains only local (within the scope of method), however, if the string variable is passed as a reference (i.e. with 'ref' keyword), the change of address would reflect outside the scope of called method as well.
Now another question is:
What if I want to access the address of the string variable when 'passed by value' and would want to change the data-value that change in value gets reflected even outside the scope of the called method. Can someone show, how would that be possible?
You got it right.
For your question - If I understood it correctly -, you can do that in an unsafe context (You should compile with -unsafe flag). I wrote the following example to show you the details you want, it's pretty long but I hope it will give you the information you need:
using System;
namespace ConsoleApplication1
{
public class Program
{
private static unsafe void Main(string[] args)
{
string s = "OldString"; // A string, which is reference-type
string ss = s; // Another string variable that refers to the old string, they both have same memory address.
fixed (char* p = s) // p is a pointer to the first char in the string.
{
Console.WriteLine($"Address of first char inside Main before calling ChangeString: {new IntPtr(p)}"); // print the address
}
ChangeString(s); // this function is pass by value to a reference type, but it actually changes the string, not creating a new one.
fixed (char* p = s)
{
Console.WriteLine($"Address of first char inside Main after calling ChangeString: {new IntPtr(p)}"); // print the address again after the ChangeString call, it will be the same old address.
}
Console.WriteLine($"s in main after ChangeString: {s}"); // The change in ChangeString is reflected in Main.
Console.WriteLine($"ss in main after ChangeString: {ss}"); // It also reflects to ss because both s and ss have the same reference.
s = "Totally new object with new address"; // This creates a new string object.
fixed (char* p = s)
{
Console.WriteLine($"Address of first char inside Main after creating new string: {new IntPtr(p)}"); // print the address, it will be different one.
}
}
private static unsafe void ChangeString(string str)
{
Console.WriteLine($"In ChangeString before: {str}"); // print the passed string.
fixed (char* p = str)
{
Console.WriteLine($"Address of first char inside ChangeString: {new IntPtr(p)}"); // print the address.
p[0] = 'N'; // change the first char.
p[1] = 'e'; // change the second char.
p[2] = 'w'; // change the third char.
// In the previous lines, I'm here changing the string, not creating a new string.
}
Console.WriteLine($"In ChangeString after: {str}"); // the string is changed here and reflects in main.
}
}
}
/cc @BillWagner, to check the accuracy of information I'm providing here.
@Youssef1313: Thank you much for explaining in such great detail. Now, this topic has gone in more depths, and this conversation will provide more insights to all the readers like me.
@rubbalbhusri, You're welcome :)
If the goal is to change a string value, and have that visible outside of the method, you can also use pass by ref, as the following sample shows:
using System;
public class Program
{
public static void Main()
{
var message = "Hello world!";
Console.WriteLine(message); // prints "Hello world!"
changeMessage(ref message);
Console.WriteLine(message); // prints "This has been changed"
}
private static void changeMessage(ref string message)
{
message = "This has been changed";
}
}
I think we've covered all the nuances here. I'll close this as answered.
Most helpful comment
You got it right.
For your question - If I understood it correctly -, you can do that in an unsafe context (You should compile with
-unsafeflag). I wrote the following example to show you the details you want, it's pretty long but I hope it will give you the information you need:/cc @BillWagner, to check the accuracy of information I'm providing here.