再看第(
)段代码生成的指令
locals init ([] string s)
IL_: nop
IL_: ldstr abcd
IL_: stloc
IL_: ret
可以很清楚地看到第()段代码将导致String类的Concat()方法被调用(实现字串加法运算)对于第()段代码由于C#编译器聪明地在编译时直接将两个字串合并为一个字串字面量所以程序运行时CLR只调用一次ldstr指令就完成了所有工作其执行速度谁快就不言而喻了!
避免使用加法运算符连接不同类型的数据
请看以下代码
String str = += + ;
ConsoleWriteline(str);
生成的MSIL指令为
maxstack
locals init ([] string str)
IL_: nop
IL_: ldstr +=
IL_: ldci xc
IL_b: box [mscorlib]SystemInt
IL_: call string [mscorlib]SystemString::Concat(object
object)
IL_: stloc
IL_: ldloc
IL_: call void [mscorlib]SystemConsole::WriteLine(string)
IL_c: nop
IL_d: ret
可以清晰地看到这两句C#代码不仅导致了String类的Concat()方法被调用(IL_)而且还引发了装箱操作(IL_b)!
Concat()方法会导致CLR为新字串分配内存空间而装箱操作不仅要分配内存还需要创建一个匿名对象对象创建之后还必须有一个数据复制的过程代价不菲!
改为以下代码
String str = +=;
ConsoleWrite(str);
ConsoleWriteLine();
生成的MSIL指令为
maxstack
locals init ([] string str)
IL_: nop
IL_: ldstr +=
IL_: stloc
IL_: ldloc
IL_: call void [mscorlib]SystemConsole::Write(string)
IL_d: nop
IL_e: ldci xc
IL_: call void [mscorlib]SystemConsole::WriteLine(int)
IL_: nop
IL_: ret
可以看到虽然多了一次方法调用(ConsoleWrite)方法但却避免了复杂的装箱操作也避免了调用StringConcat()方法对内存的频繁分配操作性能更好
.在循环中使用StringBuilder代替String实现字串连接
在某些场合需要动态地将多个子串连接成一个大字串比如许多复杂的SQL命令都是通过循环语句生成的这时应避免使用String类的加法运算符举个简单的实例
String str =
;
for (int i = ; i <= ; i++)
{
str += i;
if(i<)
str += +;
}
上述代码将生成一个字串++…+
有了前面的知识读者一定知道这将导致进行次装箱操作次字串内存分配操作(由StringConcat()方法引发)由于生成的MSIL指令太长此处不再列出请读者自行用ildasmexe工具查看上述代码生成的MSIL指令
改为以下代码程序性能会好很多
//预先分配K的内存空间
StringBuilder sb = new StringBuilder();
for (int i = ; i <= ; i++)
{
sbAppend(i);
if(i<)
sbAppend(+);
}
String result = sbToString();
通过使用ildasmexe工具查看生成的MSIL代码发现虽然上述代码生成的MSIL指令比前面多了条但却避免了耗时的装箱操作而且内存分配的次数也少了很多当循环的次数很大时两段代码的运行性能差异很大
[] []