我今天读到,在c#中字符串是不可变的,就像一旦创建它们就无法改变,那么代码如何工作
string str="a";
str +="b";
str +="c";
str +="d";
str +="e";
console.write(str) //output: abcde
为什么变量的值会发生变化?
答案 0 :(得分:14)
字符串对象是不可变的,但可以重新分配变量。
您创建了单独的对象
a
ab
abc
abcd
abcde
这些不可变的字符串中的每一个又被分配给变量str。
您无法更改字符串中的内容(字符内)。
改变变量完全是另一回事。
答案 1 :(得分:7)
很容易证明帖子确实不将String对象变为相信:
string a = "a";
string b = a;
Console.WriteLine(object.ReferenceEquals(a, b)); // true
Console.WriteLine(a); // a
Console.WriteLine(b); // a
b += "b";
Console.WriteLine(object.ReferenceEquals(a, b)); // false
Console.WriteLine(a); // a
Console.WriteLine(b); // ab
这是因为x += y
运算符等同于x = x + y
,但输入次数较少。
快乐的编码。
答案 2 :(得分:4)
使用反射器查看ILM代码,您将看到究竟发生了什么。虽然您的代码在逻辑上将新内容附加到字符串的末尾,但在后台编译器正在创建ILM代码,该代码为每个赋值创建一个新字符串。
如果您在单个语句中连接文字字符串,那么图片会变得更加混乱:
str = "a" + "b" + "c" ...
在这种情况下,编译器通常足够聪明,不能创建所有额外的字符串(因此适用于垃圾收集器,并将它转换为ILM代码,相当于:
str = "abc"
也就是说,在这样的单独行上执行此操作可能不会触发优化。
答案 3 :(得分:4)
不可变意味着用于存储字符串变量的内存位置永远不会被更改。
string str ="a"; //stored at some memory location
str+= "b"; // now str='ab' and stored at some other memory location
str+= "c"; // now str='abc' and stored at some other memory location
依旧......
每当您更改字符串类型的值时,您实际上从未将新值存储在原始位置,而是将其存储在新的内存位置。
string a="Hello";
string b=a;
a="changed";
console.writeline(b);
您好// variable b
仍然指的是原始位置。
请查看John Skeet的页面
答案 4 :(得分:2)
<强>概念强>
变量和实例是不同的概念。变量是保存值的东西。在字符串的情况下,变量保存指向存储在其他地方的字符串的指针:实例。
如果你想要的话,总是可以分配和重新分配一个变量......毕竟它是可变的! =)
我告诉的实例是在其他地方,在字符串的情况下无法更改。
通过像你一样连接字符串,你实际上创建了许多不同的存储,每个字符串连接一个。
正确的方法:
要连接字符串,可以使用StringBuilder类:
StringBuilder b = new StringBuilder();
b.Append("abcd");
b.Append(" more text");
string result = b.ToString();
您也可以使用字符串列表,然后加入它:
List<string> l = new List<string>();
l.Add("abcd");
l.Add(" more text");
string result = string.Join("", l);
答案 5 :(得分:1)
它没有,它实际上是用新值覆盖变量,每次都创建一个新的字符串对象。
答案 6 :(得分:1)
不变性意味着该类无法修改。对于每个+ =,您将创建一个全新的字符串对象。
答案 7 :(得分:1)
@pst - 我同意可读性很重要,并且在大多数情况下在PC上都无关紧要,但如果你在一个系统资源有限的移动平台上呢?
了解StringBuilder 是连接字符串的最佳方式非常重要。 速度更快,效率更高。
但是,您强调了重要的区别,即它是否显着更快,以及在什么情况下。这说明差异必须以低音量的刻度来衡量,因为它无法以毫秒为单位进行测量。
重要的是要知道,对于桌面平台上的日常场景,差异是难以察觉的。但同样重要的是要知道,对于移动平台,您正在构建大型字符串或执行数千次会话的边缘情况,或者为了性能优化,StringBuilder确实获胜。有了大量的concats,值得注意的是StringBuilder需要更多的内存。
这绝不是一个完美的比较,但对于那个连续1,000,000个字符串的傻瓜,StringBuilder比普通的字符串串联大约10分钟(在Win 7 x64的Core 2 Duo E8500 @ 3.16GHz上):
String concat (10): 9 ticks, 0 ms, 8192 bytes
String Builder (10): 2 ticks, 0 ms, 8192 bytes
String concat (100): 30 ticks, 0 ms, 16384 bytes
String Builder (100): 6 ticks, 0 ms, 8192 bytes
String concat (1000): 1658 ticks, 0 ms, 1021964 bytes
String Builder (1000): 29 ticks, 0 ms, 8192 bytes
String concat (10000): 105451 ticks, 34 ms, 2730396 bytes
String Builder (10000): 299 ticks, 0 ms, 40624 bytes
String concat (100000): 15908144 ticks, 5157 ms, 200020 bytes
String Builder (100000): 2776 ticks, 0 ms, 216888 bytes
String concat (1000000): 1847164850 ticks, 598804 ms, 1999804 bytes
String Builder (1000000): 27339 ticks, 8 ms, 2011576 bytes
代码:
class Program
{
static void Main(string[] args)
{
TestStringCat(10);
TestStringBuilder(10);
TestStringCat(100);
TestStringBuilder(100);
TestStringCat(1000);
TestStringBuilder(1000);
TestStringCat(10000);
TestStringBuilder(10000);
TestStringCat(100000);
TestStringBuilder(100000);
TestStringCat(1000000);
TestStringBuilder(1000000);
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
static void TestStringCat(int iterations)
{
GC.Collect();
String s = String.Empty;
long memory = GC.GetTotalMemory(true);
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
s += "a";
}
sw.Stop();
memory = GC.GetTotalMemory(false) - memory;
Console.WriteLine("String concat \t({0}):\t\t{1} ticks,\t{2} ms,\t\t{3} bytes", iterations, sw.ElapsedTicks, sw.ElapsedMilliseconds, memory);
}
static void TestStringBuilder(int iterations)
{
GC.Collect();
StringBuilder sb = new StringBuilder();
long memory = GC.GetTotalMemory(true);
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
sb.Append("a");
}
sw.Stop();
memory = GC.GetTotalMemory(false) - memory;
Console.WriteLine("String Builder \t({0}):\t\t{1} ticks,\t{2} ms,\t\t{3} bytes", iterations, sw.ElapsedTicks, sw.ElapsedMilliseconds, memory);
}
}