使用StringBuilder获取C#中的常量字符串?

时间:2019-07-10 14:19:33

标签: c# string stringbuilder

在我当前的项目中,他们将每个SQL查询都放在“ QueryList”类中​​,每个查询都是一个不同的Get方法。 我不明白的是,它们在这些方法中使用StringBuilder来构建常量字符串,没有在其中使用任何操作或串联。当我对此提出疑问时,他们说,最好使用串联。 据我所知,字符串文字的串联由编译器解决,对吗?编译器中是否也对此类StringBuilder代码进行了优化?

我在Java(Stringbuilder for Constants)中看到了类似的问题,对于C#应该是相同的吗?

public static class QueryList
{
    private static StringBuilder GetQuery
    {
        get
        {
            StringBuilder query = new StringBuilder();

            query.Append(" INSERT INTO ");
            query.Append("  MyTable  ");
            query.Append("  (column1, column2)  ");
            query.Append(" VALUES ");
            query.Append("  (@val1 , @val2) ");

            return query;
        }
    }
}

然后称为

string query = QueryList.GetQuery.ToString();

我计算了一下,大约有700次调用这些方法的方法,所有方法后均带有“ .ToString();”。 我知道在进行实际的字符串连接时会更好,但是只有34个需要它的调用。 该程序每秒大约有200个查询。

3 个答案:

答案 0 :(得分:6)

如果要返回常量 string,只需执行以下操作:

    // I've removed "Get" from the name
    private const string Query = 
      @"INSERT INTO MyTable (
          column1, 
          column2)
        VALUES (
          @val1, 
          @val2)";

StringBuilder中不需要

答案 1 :(得分:4)

  

编译器中是否对此类StringBuilder代码进行了优化

不。它将每次运行。

没有完美的方法将多行字符串文字嵌入C#(不幸的是)。在这些选项中,这是我最喜欢的SQL查询:

        private static string SomeQuery
        {
            get
            {
                var query = @"
INSERT INTO
MyTable (column1, column2)
values (@val1, @val2)
";
                return query;
            }
        }

答案 2 :(得分:1)

这里没有优化-使用StringBuilder不仅毫无意义,而且会严重损害代码的效率。有问题的属性将始终返回以下字符串:

INSERT INTO   MyTable    (column1, column2)   VALUES   (@val1 , @val2)

话虽如此,如果您要连接多个字符串文字,任何现代C#编译器都会意识到它应该在编译时(而不是运行时)执行此操作。通过示例,请考虑以下代码:

void Main()
{
    string a = "a" + "b" + "c";
    string b = "def";

    Console.WriteLine(a);
    Console.WriteLine(b);
}

使用LINQpad 5,它将编译为以下IL:

IL_0000:  nop         
IL_0001:  ldstr       "abc"
IL_0006:  stloc.0     // a
IL_0007:  ldstr       "def"
IL_000C:  stloc.1     // b
IL_000D:  ldloc.0     // a
IL_000E:  call        System.Console.WriteLine
IL_0013:  nop         
IL_0014:  ldloc.1     // b
IL_0015:  call        System.Console.WriteLine
IL_001A:  nop         
IL_001B:  ret 

请特别注意"a" + "b" + "c""def"都产生完全相同的IL-换句话说,该工具足够聪明,可以意识到"a" + "b" + "c"与{{ 1}}。

现在,考虑以下代码:

"abc"

这表示以下IL:

void Main()
{
    var a = new StringBuilder();
    a.Append("a");
    a.Append("b");
    a.Append("c");
    string b = "def";

    Console.WriteLine(a.ToString());
    Console.WriteLine(b);
}

这是25条指令,而不是12条指令-换句话说,所谓的“更好”的代码实际上导致了两倍以上的IL和更大的内存消耗。

话虽如此,只需使用文字IL_0000: nop IL_0001: newobj System.Text.StringBuilder..ctor IL_0006: stloc.0 // a IL_0007: ldloc.0 // a IL_0008: ldstr "a" IL_000D: callvirt System.Text.StringBuilder.Append IL_0012: pop IL_0013: ldloc.0 // a IL_0014: ldstr "b" IL_0019: callvirt System.Text.StringBuilder.Append IL_001E: pop IL_001F: ldloc.0 // a IL_0020: ldstr "c" IL_0025: callvirt System.Text.StringBuilder.Append IL_002A: pop IL_002B: ldstr "def" IL_0030: stloc.1 // b IL_0031: ldloc.0 // a IL_0032: callvirt System.Object.ToString IL_0037: call System.Console.WriteLine IL_003C: nop IL_003D: ldloc.1 // b IL_003E: call System.Console.WriteLine IL_0043: nop IL_0044: ret (正如其他答案中已经指出的那样)。

在这里返回StringBuilder也是很奇怪的,除非他们打算以后再向字符串中添加更多内容。如果他们不这样做,则应该返回一个字符串。或者,在这种情况下,就像@David Browne指出的那样,字符串常量会更好。

这一点可能更具争议性,但是将其设置为属性而不是公共常量是没有意义的,并且只会增加不必要的开销。属性最终是方法调用的语法糖,而const string允许编译器在编译时进行文本替换,而不必进行运行时方法调用。

TL; DR 仅使用const string效率更高,因为示例中显示的代码会强制您的程序在运行时执行某些工作,而这些工作可以在编译时轻松完成。< / p>