作为类成员的StringBuilder?

5

在类中将StringBuilder作为成员变量,在类方法中使用它来连接字符串是否高效(或必要)?

还是在这些操作之前使用new StringBuilder()

如果有一个可用的StringBuilder,我认为这样做是高效的。

Xing


11
新的StringBuilder初始化并不昂贵,不要优化错误的部分。 - Tim Schmelter
2
根据使用情况而定。但是分配StringBuilder不应该太昂贵。如果这是一个问题,您可以随时创建一个构建器池来使用。通常,如果您想要线程安全性,则创建类级成员会受到影响。 - Yuval Itzchakov
这真的取决于情况... 如果你有很多方法(比如数百个,这对我来说没有太多意义),那么拥有一个成员是有意义的... 但只要这种情况发生几次,开销就真的可以忽略不计了。 - gregkalapos
1
在内存方面,当你需要时创建和释放一个 StringBuilder 会更有效率 - 如果你将其作为类成员,则它不会超出范围,直到对象结束,而有些对象可能会存在很长时间。 - slugster
2
你问错了问题。性能不应该是关键,而应该考虑其他原因,比如线程安全(使用局部变量而不是类成员)或者如果需要从其他方法访问 StringBuilder 的选项,这是在使用局部变量时不可能的。通常情况下,我会尽可能地限制变量的范围和可访问性。 - Tim Schmelter
基准测试针对这个确切的问题:http://www.dotnetperls.com/stringbuilder-cache(但在最终结果部分有一个错别字,缓存版本实际上更快) - Baldrick
1个回答

4

这是一个有趣的问题,已经研究过。

答案是,在极端测试条件下共享单个实例并重复使用比每次创建新的StringBuilder更有效率,但在大多数情况下几乎不需要这样做,而且可能是一个坏主意。其他考虑因素如线程安全通常比过早进行微观优化更为重要。

以下代码来自链接文章,进行了高度人为的性能测试:

using System;
using System.Diagnostics;
using System.Text;

class Program
{
    static string Method1()
    {
        StringBuilder builder = new StringBuilder();
        foreach (string value in _array)
            builder.Append(value);
        return builder.ToString();
    }

    static StringBuilder _builder = new StringBuilder();

    static string Method2()
    {
        StringBuilder builder = _builder;
        builder.Clear();
        foreach (string value in _array)
            builder.Append(value);
        return builder.ToString();
    }

    static string[] _array = { "dot", "net", "perls", "string", "array" };
    const int _max = 1000000;
    static void Main()
    {
        Method1();
        Method2();

        // Version 1: use new StringBuilder each time.
        var s1 = Stopwatch.StartNew();
        for (int i = 0; i < _max; i++)
        {
            Method1();
        }
        s1.Stop();

        // Version 2: reuse a single StringBuilder instance.
        var s2 = Stopwatch.StartNew();
        for (int i = 0; i < _max; i++)
        {
            Method2();
        }
        s2.Stop();
        Console.WriteLine(s1.Elapsed.TotalMilliseconds);
        Console.WriteLine(s2.Elapsed.TotalMilliseconds);
        Console.Read();
    }
}

以下是我刚刚运行的结果:

  • 重复分配: 146.21
  • 将StringBuilder作为字段: 98.96

3
这是一个微观优化问题,实际上并不重要。大多数情况下应该考虑其他因素,比如可读性、可维护性、(线程)安全性、可访问性、封装性和可重用性。此外,通常还有其他可以改进的方面。我不认为有必要对使用大型循环的方法进行数百万次调用。相反,可以使用一个带有更大循环计数器的单个循环。(例如)。 - Tim Schmelter
@TimSchmelter:我完全同意——但作为一个学术问题,它仍然很有趣。我在我的答案中指出,在几乎任何实际情况下,这都不会是相关的。 - Baldrick
1
是的,但你要记住的是,出于性能原因使用单个StringBuilder确实更好,但实际上并非如此。因此,人们会使用一个类成员StringBuilder,没有性能提升,但可能会在未来出现线程问题,或者如果一个方法操作内容而另一个方法不希望它,则会产生错误结果。这引入了不必要的复杂性和依赖关系。StringBuilder是创建字符串的工具。由于它是该类型的属性,而不是StringBuilder本身的属性,因此可以将其存储为类中的字段。 - Tim Schmelter
RAM并不比你的时间更昂贵,我更喜欢可维护性和可读性,而不是少分配48个单位的内存。 - mehmet mecek
1
@TimSchmelter:我理解你的观点,但我不确定我完全同意。我尽力准确回答人们的问题,并提供所有信息,让他们作为程序员自己做决定。这样更有效吗?是的...你怎么知道呢?这是代码...这是否意味着这是个好主意?不是,这就是为什么...现在你决定该怎么做... - Baldrick

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接