如果字符串在c#中是不可变的,我怎么会这样做?

时间:2011-08-03 02:43:31

标签: c#

我今天读到,在c#中字符串是不可变的,就像一旦创建它们就无法改变,那么代码如何工作

string str="a";
str +="b";
str +="c";
str +="d";
str +="e";

console.write(str) //output: abcde

为什么变量的值会发生变化?

8 个答案:

答案 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的页面

http://csharpindepth.com/Articles/General/Strings.aspx

答案 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);
        }
    }