如何在.NET中传递string?
当我传递一个string
到一个函数,是一个指向string的内容传递,或者是整个string传递给堆栈上的函数就像一个struct
?
要回答你的问题,请考虑下面的代码:
void Main() { string strMain = "main"; DoSomething(strMain); Console.Write(strMain); // What gets printed? } void DoSomething(string strLocal) { strLocal = "local"; }
有三件事你需要知道,以预测这里会发生什么,并理解它为什么会这样做。
- string是C#中的引用types。 但这只是图片的一部分。
- 它们也是不变的,所以任何时候你做一些看起来像是在改变string的东西,你都不会。 一个全新的string被创build,引用指向它,旧的被扔掉。
- 即使string是引用types,
strMain
也不会被引用传递。 这是一个引用types,但引用是通过值传递的 。 这是一个棘手的区别,但这是一个至关重要的区别。 任何时候你传递一个没有ref
关键字的参数(不包括参数),你已经传递了一些值。
但是,这是什么意思?
按值传递引用types:你已经在做这件事了
C#中有两组数据types: 引用types和值types 。 还有两种方式在C#中传递参数: 通过引用和值 。 这些听起来是一样的,很容易混淆。 他们不是一回事!
如果您传递了任何types的参数,并且不使用ref
关键字,那么您已经通过值传递了它。 如果你已经通过它的价值,你真的通过一个副本。 但是, 如果参数是一个引用types,那么你复制的东西就是引用,而不是它指向的东西。
这是我们Main
方法的第一行:
string strMain = "main";
实际上,我们在这一行创build了两件事情:一个string,其值main
存储在内存中,另一个名为strMain
的引用variables指向它。
DoSomething(strMain);
现在我们把这个引用传递给DoSomething
。 我们已经通过它的价值,所以这意味着我们做了一个副本。 但是它是一个引用types,所以这意味着我们复制引用,而不是string本身。 现在我们有两个引用,每个引用指向内存中的相同值。
在被调用者内部
以下是DoSomething
方法的顶部:
void DoSomething(string strLocal)
没有ref
关键字,像往常一样。 所以strLocal
不是strMain
,但它们都指向相同的地方。 如果我们“改变” strLocal
,像这样…
strLocal = "local";
…我们并没有改变储值。 我们已经重新指出了参考。 我们把这个引用叫做strLocal
并把它定位在一个全新的string上。 当我们这样做时, strMain
会发生什么? 没有。 它仍然指着旧的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"
不变性很重要
让我们改变一下情景。 想象一下,我们不使用string,而是使用一些可变的引用types,就像创build的类一样。
class MutableThing { public int ChangeMe { get; set; } }
如果您按照引用objLocal
指向它所指向的对象,则可以更改其属性:
void DoSomething(MutableThing objLocal) { objLocal.ChangeMe = 0; }
内存中仍然只有一个MutableThing
,复制参考和原始参考仍然指向它。 MutableThing
本身的属性已经改变了 :
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 }
啊,但是…
string是不可改变的! 没有ChangeMe
属性设置。 你不能做strLocal[3] = 'H';
像你可以用C风格的字符数组; 你必须build立一个全新的string。 更改strLocal
的唯一方法是将引用指向另一个string,这意味着您对strLocal
执行的任何操作都strLocal
影响strMain
。 该值是不可变的,并且引用是副本。
因此,即使string是引用types,通过值传递它们意味着被调用者的任何事情都不会影响调用者中的string。 但是因为它们是引用types,所以当你想要传递它的时候,你不需要在内存中复制整个string。
更多资源:
- 这里是我读过的有关C#中引用types和值types之间差异的最好的文章,以及为什么引用types与引用传递参数不相同。
- 像往常一样,埃里克·利珀特也有这个问题上的几个优秀的博客文章 。
- 他也有一些不变的东西 。
C#中的string是不可变的引用对象。 这意味着对它们的引用被传递(按值),一旦创build了一个string,就不能修改它。 产生修改版本的string(子string,修剪版本等)的方法创build原始string的修改后的副本 。
string是特殊情况。 每个实例都是不可变的。 当你改变一个string的值时,你在内存中分配一个新的string。
所以只有引用被传递给你的函数,但是当编辑这个string时,它将成为一个新的实例,不会修改旧的实例。
在C#中,除非使用关键字“ref”通过引用传递,否则所有简单数据types都通过值传递。 系统和自定义类实例通常通过引用传递。