如何在.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"; } 

有三件事你需要知道,以预测这里会发生什么,并理解它为什么会这样做。

  1. string是C#中的引用types。 但这只是图片的一部分。
  2. 它们也是不变的,所以任何时候你做一些看起来像是在改变string的东西,你都不会。 一个全新的string被创build,引用指向它,旧的被扔掉。
  3. 即使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都通过值传递。 系统和自定义类实例通常通过引用传递。