在我当前的项目中,他们将每个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个查询。
答案 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>