19

如果我在方法中使用 StringBuilder 对象构建字符串,是否有意义:

返回 StringBuilder 对象,让调用代码调用 ToString()?

return sb;

或通过自己调用 ToString() 返回字符串。

return sb.ToString();

我想如果我们返回小字符串或大字符串会有所不同。在每种情况下什么是合适的?提前致谢。

编辑:我不打算进一步修改调用代码中的字符串,但好点 Colin Burnett。

主要是返回StringBuilder对象还是字符串更有效?会返回对字符串的引用还是副本?

4

11 回答 11

24

如果要进一步修改字符串,则返回 StringBuilder,否则返回字符串。这是一个 API 问题。

关于效率。由于这是一个没有任何细节的模糊/一般性问题,所以我认为可变与不可变比性能更重要。可变性是让您的 API 返回可修改对象的 API 问题。字符串长度与此无关。

那就是说。如果您使用 Reflector 查看 StringBuilder.ToString:

public override string ToString()
{
    string stringValue = this.m_StringValue;
    if (this.m_currentThread != Thread.InternalGetCurrentThread())
    {
        return string.InternalCopy(stringValue);
    }
    if ((2 * stringValue.Length) < stringValue.ArrayLength)
    {
        return string.InternalCopy(stringValue);
    }
    stringValue.ClearPostNullChar();
    this.m_currentThread = IntPtr.Zero;
    return stringValue;
}

您可以看到它可能会创建一个副本,但是如果您使用 StringBuilder 对其进行修改,那么它将创建一个副本(这就是我可以告诉 m_currentThread 的重点是因为 Append 会检查它并在它与当前线程不匹配时将其复制) .

我想这件事的结束是,如果你不修改 StringBuilder ,那么你就不会复制字符串,长度与效率无关(除非你达到第二个 if)。

更新

System.String 是一个类,这意味着它是一个引用类型(而不是值类型),所以“string foo;” 本质上是一个指针。(当您将字符串传递给方法时,它传递的是指针,而不是副本。) System.String 在 mscorlib 内部是可变的,但在它外部是不可变的,这就是 StringBuilder 操作字符串的方式。

因此,当调用 ToString() 时,它通过引用返回其内部字符串对象。此时您无法修改它,因为您的代码不在 mscorlib 中。通过将 m_currentThread 字段设置为零,StringBuilder 上的任何进一步操作都将导致它复制字符串对象,以便可以修改它而不修改它在 ToString() 中返回的字符串对象。考虑一下:

StringBuilder sb = new StringBuilder();
sb.Append("Hello ");

string foo = sb.ToString();

sb.Append("World");

string bar = sb.ToString();

如果 StringBuilder 没有复制,那么最后 foo 将是“Hello World”,因为 StringBuilder 修改了它。但既然它确实做了一个副本,那么 foo 仍然只是“Hello”,而 bar 是“Hello World”。

这是否澄清了整个返回/参考的事情?

于 2009-05-07T12:55:00.450 回答
5

我认为性能不应该是这个问题的一个因素。无论哪种方式,都会有人调用 sb.ToString() 所以你会在某个地方受到打击。

更重要的问题是方法的意图和目的是什么。如果此方法是构建器的一部分,您可能会返回字符串构建器。否则我会返回一个字符串。

如果这是公共 API 的一部分,我会倾向于返回字符串而不是构建器。

于 2009-05-07T12:55:30.353 回答
3

我会说该方法应该返回 sb.ToString()。如果围绕创建 StringBuilder() 对象的逻辑应该在未来发生变化,对我来说,在方法中更改它是有意义的,而不是在调用该方法然后继续做其他事情的每个场景中

于 2009-05-07T12:55:32.757 回答
3

StringBuilder是您的方法的实现细节。您应该返回字符串,直到它成为性能问题,此时您应该探索另一种模式(如访问者模式),它可以帮助您引入间接并保护您免受内部实现决策的影响。

字符串始终存储在堆中,因此如果返回类型为字符串,您将返回一个引用。但是,您不能指望两个相同的字符串具有相同的引用。通常,将字符串视为值类型是安全的,即使它实际上是引用类型。

于 2009-05-07T13:05:08.380 回答
1

这取决于您计划对输出做什么。我会亲自返回一个字符串。这样,如果您需要在以后更改方法以不使用字符串生成器,则可以,因为您不会将其作为返回值。

想了一会儿,答案就清楚多了。问哪个应该返回的问题确实回答了这个问题。返回对象应该是一个字符串。原因是,如果您问这个问题,“是否有理由在字符串可行时返回 StringBuilder 对象?” 那么答案是否定的。如果有原因,那么返回字符串是不可能的,因为需要 stringbuilder 的方法和属性。

于 2009-05-07T12:55:55.220 回答
1

我认为这取决于您在离开方法后对字符串执行的操作。如果您要继续附加到它,那么您可能需要考虑返回一个 stringbuilder 以提高效率。如果您总是要在其上调用 .ToString() ,那么您应该在方法内部执行此操作以更好地封装。

于 2009-05-07T12:56:21.333 回答
1

在几乎所有情况下,我都会返回 a string,特别是如果该方法是公共 API 的一部分。

如果您的方法只是更大的私有“构建器”进程的一部分,并且调用代码将进行进一步的操作,则例外情况。在那种情况下,我可能会考虑返回一个StringBuilder.

于 2009-05-07T12:57:36.340 回答
1

既然你不会再修改它了

return sb.ToString();

应该是最有效的

于 2009-05-07T13:01:04.600 回答
1

返回 sb.ToString()。您的方法应该只专注于手头的东西(在这种情况下为我构建一个字符串)而不是返回以进一步操作 IMO,您可能会遇到各种问题,因为它没有被处理。

于 2009-05-07T13:03:41.473 回答
0

如果您需要向字符串附加更多内容并使用其他与字符串生成器相关的功能,请返回字符串生成器。否则,如果您只是使用字符串本身,请返回该字符串。

还有其他更多的技术考虑,但这是最高级别的问题。

于 2009-05-07T12:55:40.213 回答
0

该方法被赋予了一个具体的任务,应该期望它完成它并返回不需要进一步处理的完成结果。仅在您真正需要时才返回 StringBuilder。在这种情况下,还要在方法名称中添加一些内容以表明您正在返回一些特殊的内容。

于 2009-05-07T13:16:08.870 回答