C#中的可变字符串和不可变字符串有什么区别?

Ven*_*esh 188 c# string

C#中的可变字符串和不可变字符串有什么区别?

Ste*_*n C 312

可变和不可变的英语单词分别表示"可以改变"和"不能改变".在IT环境中,单词的含义是相同的; 即

  • 一个可变的字符串可以改变,和
  • 不可变的字符串无法更改.

这些单词的含义在C#/ .NET中与在其他编程语言/环境中相同,但(显然)类型的名称可能不同,其他细节也可能不同.


作为记录:

  • String 是标准的C#/ .Net不可变字符串类型
  • StringBuilder 是标准的C#/ .Net可变字符串类型

要在表示为C#的字符串上"实现更改" String,您实际上会创建一个新String对象.原件String没有改变......因为它是不可改变的.

在大多数情况下,最好使用String它,因为它更容易理由; 例如,你不需要考虑其他线程可能"改变我的字符串"的可能性.但是,当您需要使用一系列操作构造或修改字符串时,使用a可能更有效StringBuilder.


最后,对于那些断言a StringBuilder不是字符串因为它不是不可变的人,Microsoft 文档描述StringBuilder如下:

"表示可变字符串.此类不能被继承."


Pan*_*wal 181

String 是不可改变的

即字符串不能改变.当您更改字符串(例如通过添加它)时,您实际上是在创建一个新字符串.

StringBuilder不是一成不变的(相反,它是可变的)

因此,如果您必须多次更改字符串,例如多个连接,请使用StringBuilder.

  • @Rudie - 两个.每个连接都会创建一个新的String对象,并复制两个输入字符串的所有字符.导致'O(N ^ 2)`行为...... (12认同)
  • **如果你需要多次更改字符串,例如多个连接,那么请使用StringBuilder**...这让我完全清楚了... (5认同)
  • 如果多次连接不可变字符串会发生什么?没有引用的字符串的内存使用量?还是慢一点? (4认同)
  • *“这完全让我头脑清醒”* - 你的意思是,就像“恢复出厂设置”?:-) (2认同)

Unm*_*kar 44

一个对象是可变的,如果一旦创建,其状态可以通过调用其上的各种操作来改变,否则它是不可变的.

不可变的字符串

在C#(和.NET)中,字符串由System.String类表示.该string关键字是这个类的别名.

System.String类是不可变的,即一旦创建它的状态不能改变.

因此,所有的操作,你上的绳子一样执行Substring,Remove,Replace,使用串联"+"操作符等将创建一个新的字符串,并将其返回.

请参阅以下程序进行演示 -

string str = "mystring";
string newString = str.Substring(2);
Console.WriteLine(newString);
Console.WriteLine(str);
Run Code Online (Sandbox Code Playgroud)

这将分别打印'string'和'mystring'.

为了不变性好处以及为什么字符串是不可变的,请检查为什么.NET String是不可变的?.

可变字符串

如果您想要经常修改一个字符串,可以使用StringBuilder该类.实例上的操作 StringBuilder 将修改同一对象.

有关何时使用的 更多建议,StringBuilder请参阅何时使用StringBuilder?.


Ada*_*erg 36

所有string对象在C#中都是不可变的.类的对象string一旦创建,就不能表示除了构造它们之外的任何值.所有似乎"更改"字符串的操作都会生成一个新字符串.这对于内存来说是低效的,但是对于能够相信字符串不会在您下面改变而非常有用 - 因为只要您不更改引用,所引用的字符串将永远不会更改.

相反,可变对象具有可以改变的数据字段.它的一个或多个方法将更改对象的内容,或者它具有一个Property,当写入时,它将更改对象的值.

如果你有一个可变对象StringBuffer- 与String最相似的对象- 那么如果你想要绝对确定它不会从你下面改变,你必须复制它.这就是为什么可变对象被用作任何形式Dictionary的对象或设置对象本身可能会改变的危险,并且数据结构无法知道,从而导致最终导致程序崩溃的损坏数据.

但是,您可以更改其内容 - 因此,与创建完整副本相比,它的内存效率要高得多,因为您想要更改单个字符或类似内容.

通常,正确的做法是在创建内容时使用可变对象,并在完成后使用不可变对象.当然,这适用于具有不可变形式的对象; 大部分藏品都没有.在将集合的内部状态发送到其他上下文时,提供只读形式的集合通常很有用,这相当于不可变 - 否则,某些东西可以获取返回值,对其执行某些操作,并破坏您的数据.


Tal*_*ner 12

不可变:

当您对某个对象执行某些操作时,它会创建一个新对象,因此状态不可修改,就像字符串一样.

易变的

当您对某个对象执行某些操作时,对象本身修改后没有像StringBuilder那样创建新的obect


cah*_*yaz 8

在.NET System.String(aka string)是一个不可变对象.这意味着当您创建对象时,您无法在之后更改其值.您只能重新创建不可变对象.

System.Text.StringBuilder是System.String的可变等价物,你可以使用它的值

例如:

class Program
{
    static void Main(string[] args)
    {

        System.String str = "inital value";
        str = "\nsecond value";
        str = "\nthird value";

        StringBuilder sb = new StringBuilder();
        sb.Append("initial value");
        sb.AppendLine("second value");
        sb.AppendLine("third value");
    }
}
Run Code Online (Sandbox Code Playgroud)

生成以下MSIL:如果您调查代码.您将看到,每当您查找System.String的对象时,您实际上是在创建一个新对象.但是在System.Text.StringBuilder中,每当更改文本的值时,都不会重新创建对象.

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       62 (0x3e)
  .maxstack  2
  .locals init ([0] string str,
           [1] class [mscorlib]System.Text.StringBuilder sb)
  IL_0000:  nop
  IL_0001:  ldstr      "inital value"
  IL_0006:  stloc.0
  IL_0007:  ldstr      "\nsecond value"
  IL_000c:  stloc.0
  IL_000d:  ldstr      "\nthird value"
  IL_0012:  stloc.0
  IL_0013:  newobj     instance void [mscorlib]System.Text.StringBuilder::.ctor()
  IL_0018:  stloc.1
  IL_0019:  ldloc.1
  IL_001a:  ldstr      "initial value"
  IL_001f:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
  IL_0024:  pop
  IL_0025:  ldloc.1
  IL_0026:  ldstr      "second value"
  IL_002b:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::AppendLine(string)
  IL_0030:  pop
  IL_0031:  ldloc.1
  IL_0032:  ldstr      "third value"
  IL_0037:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::AppendLine(string)
  IL_003c:  pop
  IL_003d:  ret
} // end of method Program::Main
Run Code Online (Sandbox Code Playgroud)

  • 嗯......反汇编代码只显示正在进行指针赋值并调用方法.它(几乎)与可变对象和不可变对象无关.(它让我感到不安,为什么有些人认为拆解一些不相关的示例代码会帮助人们理解这类事情.首先,大多数程序员都不熟悉CLI代码.) (5认同)

Rah*_*arg 6

字符串是可变的,因为.NET在场景后面使用字符串池.它的意思是 :

string name = "My Country";
string name2 = "My Country";
Run Code Online (Sandbox Code Playgroud)

name和name2都指向字符串池中的相同内存位置.现在假设您要将name2更改为:

name2 = "My Loving Country";
Run Code Online (Sandbox Code Playgroud)

它将查找字符串池中的字符串"My Loving Country",如果发现你将获得它的引用其他明智的新字符串"My Loving Country"将在字符串池中创建,name2将获得它的引用.但是整个过程" 我的国家 "没有改变,因为像名字这样的其他变量仍在使用它.这就是为什么字符串是IMMUTABLE的原因.

StringBuilder以不同的方式工作,不使用字符串池.当我们创建StringBuilder的任何实例时:

var address  = new StringBuilder(500);
Run Code Online (Sandbox Code Playgroud)

它为此实例分配大小为500字节的内存块,并且所有操作只修改此内存位置,并且此内存不与任何其他对象共享.这就是StringBuilderMUTABLE的原因.

我希望它会有所帮助.


Jam*_* Ko 5

没有,实际上。String类是可变的。

unsafe
{
    string foo = string.Copy("I am immutable.");
    fixed (char* pChar = foo)
    {
        char* pFoo = pChar;

        pFoo[5] = ' ';
        pFoo[6] = ' ';
    }

    Console.WriteLine(foo); // "I am   mutable."
}
Run Code Online (Sandbox Code Playgroud)

实际上,这种逻辑一直在String和StringBuilder类中完成。每次调用Concat,Substring等时,它们只是分配一个新字符串,并使用指针算法将其复制到新字符串。字符串本身不会发生变异,因此为什么它们被认为是“不变的”。


顺便说一句,千万不能用字符串文字尝试,或者你将严重搞乱你的程序:

string bar = "I am a string.";

fixed (char* pChar = bar)
{
    char* pBar = pChar;

    pBar[2] = ' ';
}

string baz = "I am a string.";

Console.WriteLine(baz); // "I  m a string."
Run Code Online (Sandbox Code Playgroud)

这是因为字符串文字是在桌面.NET Framework中插入的;换句话说,barbaz指向完全相同的字符串,因此更改一个字符串将更改另一个字符串。如果您使用的是WinRT这样的不受管理的平台,该平台缺少字符串内部检查,那么这一切都很好,而且花哨的。