如果一个不可变的字符串变量不能更改,为什么可以更改它呢
本文关键字:为什么 不能 变量 一个 字符串 不可变 如果 | 更新日期: 2023-09-27 17:58:34
这可能是在其他地方问的,但我只是在进行简单搜索时没有看到。我正在使用一个C#类,我知道字符串是不可变的,字符串生成器可以在整个代码中进行更改。
但是,如果字符串是不可变的,为什么C#允许您更改它
或者它允许您更改字符串,因为它占用了一个新的内存点,创建了另一个孔字符串变量,并放弃了其他变量的地址位置。如果是这种情况,并且自动垃圾收集完成并清理C#内存位置,那么一个人使用字符串还是字符串生成器真的重要吗?
代码:
string sss = 'abcd';
sss = 'ghy';
sss = 'nnnnnn';
这是通过将变量分配给不同的值来更改变量的值。
这并不是改变变量所引用的值。
您基本上已经自己回答了这个问题。简短的答案是"C#不允许您更改字符串"
由于各种设计原因,c#中的"String"被实现为引用类型。你所做的是改变ss引用的内容,而不是改变被引用的内容abcd与ghy分别存在与nnnnnn分别存在你没有把一件事变成另一件事。你正在创建一个全新的东西,然后更改ss引用的内容。
字符串在C#中是不可变的,使它们的行为类似于值类型,即使它们被实现为引用类型。为什么?因为替代方案更糟糕:
考虑一个例子,其中字符串是引用类型,但不是不可变的,您可以将"abcd"更改为"ab12"。那看起来怎么样?
string mutableString = "abcd";
string anotherRef = mutableString;
mutableString.mutateTo("ab12"); //A method that doesn't exist - for exposition only
在这一点上,另一个Ref也将是"ab12"——这似乎非常不直观。
关于字符串生成器:
StringBuilder更多的是在特殊情况下使用的性能。对于大多数字符串串联,这并不重要。如果你有两个字符串,比如"我的名字是"answers"皮特",并将它们连接起来,那么你最终会得到一个新的字符串"我的姓名是皮特",旧的字符串可能会留在记忆中的某个地方。没什么大不了的。你有你想要的完整字符串,而不是浪费很多时间或内存。不过,考虑一个更极端的例子:如果你有更多的单词:"The"quick"brown"fox"jumps"over"The"lazy"dog",你想把它们连接在一起:
- "+"quick"产生"The quick"(一个新字符串)
- "The quick"+"brown"产生"The quick brown"(另一个新字符串)
- "快速棕色"+"狐狸"产生"快速棕色狐狸"(另一个新字符串-越来越长)
- "快速褐狐"+"跳跃"产生"快速褐狐狸跳跃"(另一个新字符串-每个字符串都比上一个更贵!)
- 等等
你可以看到,这个方法会产生很多你并不真正关心的额外中间字符串,但它们都会被分配——这需要时间——并且它们都会在内存中徘徊,直到GC将它们处理掉。StringBuilder为您提供了一种方法来进行连接,而不必分配每个中间结果。
注意-下一部分并不完全正确,但对于本例来说已经足够接近了。StringBuilder的内部实现没有契约行为:您可以将其视为"懒惰的串联"。它可以在内部保留对所有组件字符串的引用,但在完成字符串的追加后调用ToString
方法之前,不会为最终结果重新分配空间。这使它能够计算所需的最终大小,并为整个组合字符串重新分配足够的空间-一次,而不是n次。