当我将string
传递给函数时,是一个指向传递的字符串内容的指针,还是整个字符串传递给堆栈上的函数,就像struct
一样?
答案 0 :(得分:246)
要回答您的问题,请考虑以下代码:
void Main()
{
string strMain = "main";
DoSomething(strMain);
Console.Write(strMain); // What gets printed?
}
void DoSomething(string strLocal)
{
strLocal = "local";
}
为了预测这里会发生什么,并了解其原因,您需要了解三件事。
strMain
也不会通过引用传递。它是引用类型,但引用按值传递。这是一个棘手的区别,但它是一个至关重要的区别。每次传递没有ref
关键字的参数(不计算out
参数)时,您都会按值传递一些内容。但这意味着什么?
C#中有两组数据类型:引用类型和值类型。还有两种方法可以在C#中传递参数:按引用和按值。这听起来一样,容易混淆。它们不是一回事!
如果您传递任何类型的参数,并且未使用ref
关键字,那么您已按值传递了它。如果你按值传递它,你真正传递的是副本。但是如果参数是引用类型,那么你复制的东西就是引用,而不是它指向的东西。
以下是Main
方法的第一行:
string strMain = "main";
我们在这一行上实际上创建了两件事:一个值为main
的字符串存储在某个地方的内存中,一个名为strMain
的引用变量指向它。
DoSomething(strMain);
现在我们将该引用传递给DoSomething
。我们按价值通过了它,这意味着我们制作了一份副本。但它是一个引用类型,因此这意味着我们复制了引用,而不是字符串本身。现在我们有两个引用,每个引用都指向内存中的相同值。
以下是DoSomething
方法的顶部:
void DoSomething(string strLocal)
像往常一样,没有ref
个关键字。因此strLocal
不是strMain
,但它们都指向同一个地方。如果我们“改变”strLocal
,就像这样......
strLocal = "local";
...我们本身没有更改存储的值。我们重新指出了这个参考。我们采用了名为strLocal
的引用,并将其指向一个全新的字符串。当我们这样做时,strMain
会发生什么? Nothing. It's still pointing at the old string!
string strMain = "main"; //Store a string, create a reference to it
DoSomething(strMain); //Reference gets copied, copy gets re-pointed
Console.Write(strMain); //The original string is still "main"
让我们改变场景一秒钟。想象一下,我们不使用字符串,而是使用一些可变引用类型,就像您创建的类一样。
class MutableThing
{
public int ChangeMe { get; set; }
}
如果您跟踪引用objLocal
到它指向的对象,您可以更改其属性:
void DoSomething(MutableThing objLocal)
{
objLocal.ChangeMe = 0;
}
内存中仍然只有一个MutableThing
,复制的引用和原始引用都指向它。 The properties of the MutableThing
itself have changed:
void Main()
{
var objMain = new MutableThing();
objMain.ChangeMe = 5;
Console.Write(objMain.ChangeMe); //it's 5 on objMain
DoSomething(objMain); //now it's 0 on objLocal
Console.Write(objMain.ChangeMe); //it's also 0 on objMain
}
<啊>啊,但......
...字符串是不可变的!没有ChangeMe
属性可供设置。你不能像使用C风格的char数组一样strLocal[3] = 'H';
;你必须改为构造一个全新的字符串。更改strLocal
的唯一方法是将引用指向另一个字符串,这意味着您对strLocal
所做的任何操作都不会影响strMain
。该值是不可变的,引用是副本。
因此即使字符串是引用类型,通过值传递它们意味着被调用者中发生的任何事情都不会影响调用者中的字符串。但由于它们是引用类型,因此当您想要传递它时,不必将整个字符串复制到内存中。
答案 1 :(得分:23)
C#中的字符串是不可变的引用对象。这意味着对它们的引用会传递(按值),一旦创建了一个字符串,就无法修改它。生成字符串的修改版本(子字符串,修剪版本等)的方法会创建原始字符串的修改后的副本。
答案 2 :(得分:10)
字符串是特殊情况。每个实例都是不可变的。当您更改字符串的值时,您将在内存中分配新字符串。
因此只有引用传递给您的函数,但是当编辑该字符串时,它将成为一个新实例,并且不会修改旧实例。