【问题标题】:Are StringBuilder strings immutable?StringBuilder 字符串是不可变的吗?
【发布时间】:2009-02-02 06:14:18
【问题描述】:

StringBuilder 被誉为比简单地连接字符串更快的字符串操作工具。不管这是否属实,我都想知道 StringBuilder 操作的结果以及它们产生的字符串。

快速浏览 Reflector 表明 StringBuilder.ToString() 并不总是返回一个副本,有时它似乎返回一个内部字符串的实例。它似乎还使用了一些内部函数来操作内部字符串。

如果我这样做,我会得到什么?

string s = "Yo Ho Ho";
StringBuilder sb = new StringBuilder(s);
string newString = sb.ToString();
sb.Append(" and a bottle of rum.");
string newNewString = sb.ToString();

newString 和 newNewString 是不同的字符串实例还是相同的?我试图通过反射器来解决这个问题,但我并不是很了解所有内容。

这段代码怎么样?

StringBuilder sb = new StringBuilder("Foo\n");
StringReader sr = new StringReader(sb.ToString());
string s = sr.ReadLine();
sb.Append("Bar\n");
s = sr.ReadLine();

最后一条语句会返回 null 还是“Bar”?如果它返回一个或另一个,这是定义的还是未定义的行为?换句话说,我可以依赖它吗?

关于这个主题的文档非常简洁,我不愿意依赖观察到的行为而不是规范。

【问题讨论】:

  • 请注意,自从提出并回答了这个问题后,StringBuilder 的实现已经发生了变化。

标签: c# .net string


【解决方案1】:

在 mscorlib 之外,System.String 的任何实例都是不可变的,句号。

StringBuilder 在内部对字符串进行了一些有趣的操作,但最终它不会向您返回字符串,然后以您的代码可见的方式对其进行变异。

至于对 StringBuilder.ToString() 的后续调用是返回相同的 String 实例还是返回具有相同值的不同 String ,这取决于实现,您不应依赖此行为。

【讨论】:

  • 是的,实验表明这是正确的,但使用 Reflector 深入研究内部结构并没有显示出明显的原因。它似乎在每次调用时返回对内部字符串的引用,因此我对字符串是否会发生变异感到困惑。
  • @Mystere,StringBuilder 的实现起初有点令人生畏。在我阅读您的问题并开始探索之前,我没有意识到他们做了线程级缓存。我计划今晚(或明天)晚些时候再看一看。但是您可以相信生成的 String 是不可变的
  • @JaredPar - 虽然这在 99.999% 的情况下都是正确的,但您 可以mscorlib... 之外拥有一个可变字符串...只要该字符串被保留。要点:gist.github.com/binarycow/304d7e23f0b1f55169e1069c829676c4
【解决方案2】:

newStringnewNewString 是不同的字符串实例。

虽然ToString() 返回当前字符串,但它会清除其当前线程变量。这意味着下次追加时,它将在追加之前获取当前字符串的副本。

我不完全确定你在第二个问题中得到了什么,但s 将为空:如果文件中的最后一个字符是行终止字符对于上一行,该行 not 被视为在这些字符和文件末尾之间有一个空行。之前读取的字符串与此无关。

【讨论】:

  • 啊……你向我解释了这个谜团。清除线程变量是导致追加副本的原因,多么简单但不明显。我的第二个问题是可变性问题的变体,与存储在 StringReader 中的引用有关。
【解决方案3】:

newStringnewNewString 不同的string 实例或 相同的?我试图弄清楚这一点 通过反射器,我只是不完全 了解一切。

它们是不同的 string 实例:newString 是“Yo Ho Ho”,newNewString 是“Yo Ho Ho and a bottle of rum.”。 strings 是不可变的,当您调用 StringBuilder.ToString() 时,该方法会返回一个不可变的 string,它表示 当前 状态。

最后一条语句会返回null 还是 “Bar”?如果它返回一个或另一个 其他,这是定义的还是未定义的 行为?换句话说,我可以依靠 有吗?

它将返回nullStringReader 正在处理您在构造函数中传递给它的不可变 string,因此它不受您对 StringBuilder 所做的任何影响。

【讨论】:

    【解决方案4】:

    这个类的全部目的是使字符串可变,实际上就是这样。我相信(但不确定)只有在没有对这个对象做任何其他事情的情况下,它才会返回进入它的相同字符串。所以之后

    String s_old = "Foo";
    StringBuilder sb = new StringBuilder(s_old);
    String s_new = sb.ToString();
    

    s_old 将与 s_new 相同,但在任何其他情况下都不会。

    我应该注意,对于 Java 编译器,Java 编译器会自动将多个字符串添加转换为使用 StringBuilder(或类似但更快的 StringBuffer)类的操作,如果 .NET 编译器也不进行这种转换,我会感到非常惊讶。

    【讨论】:

    • C# 编译器不使用 StringBuilder 进行字符串连接 - 它使用 String.Concat。这意味着在执行任何连接之前,最终长度是已知的。
    猜你喜欢
    • 2011-03-24
    • 1970-01-01
    • 1970-01-01
    • 2013-04-14
    • 1970-01-01
    • 2011-08-06
    • 1970-01-01
    • 2010-09-08
    相关资源
    最近更新 更多