C#中的字符串引用

3

由于字符串在 dotnet 中是引用类型,所以当我们更新 变量 x 时,y 的值也应该更新(因为它引用了 x 的值)。下面是一个示例程序,说明当更新 x 的值时,y 的值为什么不会改变?

public void assignment()
{
    string x= "hello";
    string y=x; // referencing x as string is ref type in .net
    x = x.Replace('h','j');
    Console.WriteLine(x); // gives "jello" output
    Console.WriteLine(y); // gives "hello" output 
}

2
不是的。改变变量的值(不影响其他变量)和改变对象的内容是有区别的。请参阅https://jonskeet.uk/csharp/references.html和https://dev59.com/MFwY5IYBdhLWcg3w9r3a#32010236 - Jon Skeet
你在谈论可变与不可变。字符串是不可变的,它始终保持相同的值。字符串也是引用类型,它没有默认分配大小。 - Bizhan
3
@Bizhan:这并不完全是关于不变性的问题。即使字符串是可变的,改变一个变量的值也不会改变另一个变量的值。(例如,假设我们使用的是 StringBuilder 而不是 string,并且疑问所在的那行代码是 x = null; - 这不会使 y 为 null。) - Jon Skeet
@JonSkeet 我认为这也与不可变性有关,因为例如 StringBuilder.Replace 会改变自身并返回自身,所以 x = x.Replace(...) 将同时更改 y 中的值。 - Charlieface
1
@Charlieface:这正是为什么我在我的示例中没有使用Replace的原因。假设string实际上是可变的,但Replace具有其当前行为,即返回一个新字符串而不是修改现有字符串。 (例如,可能会有一个ReplaceInExistingObject方法来改变它。)这个问题的行为根本不会改变 - Jon Skeet
3个回答

8

你说得对,最初,xy 都指向同一个对象:

       +-----------+
y   -> |   hello   |
       +-----------+
            ^
x   --------+

现在看一下这行:
x = x.Replace('h','j');

以下发生了什么:
  1. x.Replace creates a new string (with h replaced by j) and returns a reference to this new string.

           +-----------+    +------------+
    y   -> |   hello   |    |   jello    |
           +-----------+    +------------+
                ^
    x   --------+
    
  2. With x = ..., you assign x to this new reference. y still references the old string.

           +-----------+    +------------+
    y   -> |   hello   |    |   jello    |
           +-----------+    +------------+
                                  ^
    x   --------------------------+
    

那么如何在原地修改字符串?你不能。C#不支持在原地修改字符串。字符串被故意设计为一种不可变的数据结构。对于可变的类似字符串的数据结构,请使用StringBuilder

var x = new System.Text.StringBuilder("hello");
var y = x;

// note that we did *not* write x = ..., we modified the value in-place
x.Replace('h','j');

// both print "jello"
Console.WriteLine(x);
Console.WriteLine(y);

3

这里已经有很好的解答说明了为什么会发生这种情况。 然而,如果你想要同时打印出"jello",你可以使用 ref 关键字通过引用将 x 赋值给 y。

string x = "hello";
ref string y = ref x;
x = x.Replace('h', 'j');
Console.WriteLine(x); // gives "jello" output
Console.WriteLine(y); // gives "jello" output 

有关Ref locals的详细信息,请单击此处


1
非常好的观点,尽管我个人更喜欢称其为“将y作为x的别名”,以避免“引用赋值”的歧义。 - Heinzi

1
返回结果:

返回的字符串是一个新的字符串引用。 关于string.Replace(),MSDN上说:

“此方法不会修改当前实例的值。相反,它返回一个新字符串,在其中将所有旧值的出现替换为新值”

https://learn.microsoft.com/en-us/dotnet/api/system.string.replace?view=net-5.0

正如@Heinzi所提到的 - 字符串是不可变的,对字符串进行的大多数操作都会生成新的字符串:

“String对象是不可变的:它们在创建后不能被更改。所有看起来修改字符串的String方法和C#运算符实际上都是在新的字符串对象中返回结果”

https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/strings/#:~:text=String%20objects%20are%20immutable%3A%20they,in%20a%20new%20string%20object.

Cheers!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接