字符串对象真的是通过引用?

我正在学习(新手).NET,我有些疑惑。

从书中读到的例子我了解到String是对象,然后是Reference Type。

所以,我做了这个测试,结果与我的预期不同:

我真的很好奇,这是一个例外,因为“字符串”是特殊类型吗?

class Program { static void Main(string[] args) { SByte a = 0; Byte b = 0; Int16 c = 0; Int32 d = 0; Int64 e = 0; string s = ""; Exception ex = new Exception(); object[] types = { a, b, c, d, e, s, ex }; // C# foreach (object o in types) { string type; if (o.GetType().IsValueType) type = "Value type"; else type = "Reference Type"; Console.WriteLine("{0}: {1}", o.GetType(), type); } // Test if change string str = "I'll never will change!"; Program.changeMe(str); Console.WriteLine(str); } public static string changeMe(string param) { param = "I have changed you!!!"; return ""; // no return for test } } 

输出:

 System.SByte: Value type System.Byte: Value type System.Int16: Value type System.Int32: Value type System.Int64: Value type System.String: Reference Type System.Exception: Reference Type I'll never will change! 

String确实是一种引用类型。 但是,当您的Main方法调用changeMe(str)时,.NET会在param参数中将str的引用副本传递给changeMe。 然后,changeMe修改此副本以引用“我已经改变了你!!!”,但原始的str引用仍然指向“我永远不会改变”。

作为引用类型意味着如果您更改了传递的字符串的状态 ,则调用者将看到这些更改。 (您不能对字符串执行此操作,因为字符串是不可变的,但您可以将其用于其他引用类型,例如Control。)但是重新分配参数不会更改调用者在该参数中传递的 ,即使该值是一个参考。

字符串是参考。

changeMe不会更改字符串,只是在该函数中重新分配本地引用(指针)。

现在,如果你将字符串作为ref arg传递,你可以获得更多乐趣:

 public static string changeMe(ref string param) { param = "I have changed you!!!"; return ""; // no return for test } 

这与字符串是值类型或引用类型无关。 您的changeMe方法的参数未标记为ref ,因此如果方法更改它,则调用者将看不到更改。

试试这个:

 public static string changeMe(ref string param) { param = "I have changed you!!!"; return ""; // no return for test } 

Program.changeMe(str)不会导致str设置为“我改变了你!!!”的原因 虽然string是引用类型,但引用是按值传递的。

所以param是引用的副本,它在Program.changeMe(param)的范围内被更改,然后在方法结束时被抛弃。

参考str仅被复制,未更改为引用“我已经改变了你!!!”

字符串确实是一个特例。 它们是引用类型,但行为类似于值类型。 这是由.Net开发团队为“简单的清醒”完成的。

好的引用:它是一个引用类型一个常见的误解是string是一个值类型。 那是因为它的不变性(见下一点)使它有点像一种价值类型。 它实际上就像一个普通的引用类型。 有关值类型和引用类型之间差异的更多详细信息,请参阅有关参数传递和内存的文章。

来自: http : //www.yoda.arachsys.com/csharp/strings.html

是的,这看起来很奇怪。 发生的事情是你在参数中传递对字符串的引用,但是你要按传递它,即你在参数中创建引用的副本。 因此,当您在函数范围内重新分配它时,您只需要更改changeMe()方法的字符串引用的本地副本。

有关这方面的更多信息: http : //www.yoda.arachsys.com/csharp/parameters.html

MSDN页面可以帮助解释它为什么是引用类型。

因为Stringchar的集合并且inheritance自各种Collection基类,所以它是一个类,因此是一个引用类型。

您通过值传递并制作了引用的副本。 通过引用传递它将改变。 好吧,真的会创建一个新的字符串,你的参考将是一个新的位置。

如果您打算将参数的值更改为方法,则应该使用“ref”或“out”关键字。 否则,您可能会制造混乱的方法,这些方法可能会也可能不会达到您的意图。 正如其他人所指出的那样,你正在改变字符串引用的值而不是对象的实例。 为了看看它如何与其他类一起工作,我写了一个简单的例子

 static void Main(string[] args) { var str = "String"; var obj = new MyObject() { Value = "Object" }; Console.WriteLine(str); //String Console.WriteLine(obj); //Object ChangeMe(str); ChangeMe(obj); Console.WriteLine(str); //String Console.WriteLine(obj); //Object ChangeMeInner(obj); // this is where it can get confusing!!! Console.WriteLine(obj); //Inner ChangeMe(ref str); ChangeMe(ref obj); Console.WriteLine(str); // Changed Console.WriteLine(obj); // Changed Console.Read(); } class MyObject { public string Value { get; set; } public override string ToString() { return Value; } } static void ChangeMe(MyObject input) { input = new MyObject() { Value = "Changed" }; } static void ChangeMeInner(MyObject input) { input.Value = "Inner"; } static void ChangeMe(string input) { input = "Changed"; } static void ChangeMe(ref MyObject input) { input = new MyObject() { Value = "Changed" }; } static void ChangeMe(ref string input) { input = "Changed"; } 

对于所有其他引用类型,使用byval语义(没有ref或out)传递它意味着您无法更改调用者复制指向的堆上的哪个对象 ,这并不意味着您无法更改堆上对象的内容该变量指向。 因为字符串是不可变的,所以它们确实是一个例外,它们在这方面的行为类似于值类型,即使它们是引用类型,但这是通过CLR中的特殊附加编码来完成的。