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

时间:2010-11-25 06:07:06

标签: c# string

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

15 个答案:

答案 0 :(得分:312)

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

  • 可以更改可变字符串,
  • 无法更改不可变字符串。

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


记录:

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

要对表示为C#String的字符串进行“更改”,您实际上会创建一个新的String对象。原始String未更改...因为它不可更改。

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


最后,对于那些断言StringBuilder不是字符串因为它不是永久不变的人,Microsoft documentation描述StringBuilder因此:

  

“表示可变字符串字符。此类不能被继承。”

答案 1 :(得分:181)

String是不可变的

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

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

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

答案 2 :(得分:44)

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

不可变字符串

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

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

因此,您对SubstringRemoveReplace等字符串执行的所有操作,使用“+”运算符等进行连接都会创建一个新字符串并将其返回。

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

string str = "mystring";
string newString = str.Substring(2);
Console.WriteLine(newString);
Console.WriteLine(str);

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

对于不可变性的好处以及为什么字符串是不可变的检查Why .NET String is immutable?

Mutable String

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

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

答案 3 :(得分:36)

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

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

如果你有一个可变对象 - 与String最相似的对象是StringBuffer - 如果你想绝对确定它不会从你下面改变,你必须复制它。这就是为什么可变对象作为任何形式的Dictionary或集合使用的密钥很危险的原因 - 对象本身可能会发生变化,而数据结构无法知道,从而导致最终崩溃的数据损坏你的计划。

但是,你可以改变它的内容 - 所以它比制作一个完整的副本更多,更多的内存效率,因为你想要改变一个字符或类似的东西。

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

答案 4 :(得分:12)

永恒:

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

可变

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

答案 5 :(得分:8)

在.NET中 System.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");
    }
}

生成以下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

答案 6 :(得分:6)

字符串是可变的,因为.NET在场景后面使用字符串池。这意味着:

string name = "My Country";
string name2 = "My Country";

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

name2 = "My Loving Country";

它将查找字符串池中的字符串&#34; My Loving Country&#34;,如果找到,您将获得其他明智的新字符串&#34;我的爱国&#34;将在字符串池中创建,name2将获得它的引用。 但这整个过程&#34; 我的国家&#34;未更改,因为 name 等其他变量仍在使用它。 这就是字符串 IMMUTABLE 的原因。

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

var address  = new StringBuilder(500);

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

我希望它会有所帮助。

答案 7 :(得分: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."
}

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

顺便说一句,尝试使用字符串文字,否则你的程序会非常糟糕:

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."

这是因为字符串文字是在桌面.NET Framework中实现的;换句话说,barbaz指向完全相同的字符串,因此变异会使另一个变异。如果您使用像WinRT这样缺乏字符串实习的非托管平台,这一切都很好,花花公子。

答案 8 :(得分:2)

澄清在C#(或一般的.NET)中没有可变字符串这样的东西。其他语言支持可变字符串(可以更改的字符串),但.NET框架不支持。

所以对你的问题的正确答案是所有字符串在C#中是不可变的。

字符串具有特定含义。 “string”lowercase关键字只是从System.String类实例化的对象的快捷方式。从字符串类创建的所有对象始终是不可变的。

如果你想要一个可变的文本表示,那么你需要使用另一个类,如StringBuilder。 StringBuilder允许您迭代地构建“单词”的集合,然后将其转换为字符串(再次不可变)。

答案 9 :(得分:1)

在实施细节中。

CLR2的System.String是可变的。   StringBuilder.Append调用String.AppendInplace (私人方法)

CLR4的System.String是不可变的。   StringBuilder有Char数组和分块。

答案 10 :(得分:1)

可能无法更改数据值。注意:可以更改变量值,但丢弃原始不可变数据值并在内存中创建新数据值。

答案 11 :(得分:0)

来自http://yassershaikh.com/what-is-the-difference-between-strings-and-stringbuilder-in-c-net/

简答:字符串是不可变的 - 而StringBuilder是可变的。

这是什么意思? Wiki说:在面向对象中,不可变对象是一个对象,其状态在创建后无法修改。这与可变对象形成对比,可变对象在创建后可以修改。

从StringBuilder类文档:

String对象是不可变的。每次使用System.String类中的一个方法时,都会在内存中创建一个新的字符串对象,这需要为该新对象重新分配空间。

在需要对字符串执行重复修改的情况下,与创建新String对象相关的开销可能很昂贵。

如果要在不创建新对象的情况下修改字符串,可以使用System.Text.StringBuilder类。例如,使用StringBuilder类可以在循环中将多个字符串连接在一起时提高性能。

答案 12 :(得分:0)

以下是Immutable string和Mutable string builder

的示例
        Console.WriteLine("Mutable String Builder");
        Console.WriteLine("....................................");
        Console.WriteLine();
        StringBuilder sb = new StringBuilder("Very Good Morning");
        Console.WriteLine(sb.ToString());
        sb.Remove(0, 5);
        Console.WriteLine(sb.ToString());

        Console.WriteLine();

        Console.WriteLine("Immutable String");
        Console.WriteLine("....................................");
        Console.WriteLine();
        string s = "Very Good Morning";
        Console.WriteLine(s);
        s.Substring(0, 5);
        Console.WriteLine(s);
        Console.ReadLine();

答案 13 :(得分:0)

C#中的字符串是不可变的。如果你用任何字符串连接它,你实际上是在创建一个新的字符串,即新的字符串对象! 但StringBuilder创建了可变字符串。

答案 14 :(得分:0)

StringBuilder是连接大型数据字符串的更好选择,因为StringBuilder是可变的字符串类型,而StringBuilder对象是不可变的类型,这意味着StringBuilder在连接字符串时永远不会创建对象的新实例。

如果我们使用字符串而不是StringBuilder来实现串联,那么它将每次在内存中创建新实例。