再看第(
)段代碼生成的指令
locals init ([
] string s
)
IL_
: nop
IL_
: ldstr
abcd
IL_
: stloc
IL_
: ret
可以很清楚地看到
第(
)段代碼將導致String類的Concat()方法被調用(實現字串加法運算)
對於第(
)段代碼
由於C#編譯器聰明地在編譯時直接將兩個字串合並為一個字串字面量
所以程序運行時CLR只調用一次ldstr指令就完成了所有工作
其執行速度誰快就不言而喻了!
避免使用加法運算符連接不同類型的數據
請看以下代碼
String str =
+
=
+
;
Console
Writeline(str);
生成的MSIL指令為
maxstack
locals init ([
] string str)
IL_
: nop
IL_
: ldstr
+
=
IL_
: ldc
i
xc
IL_
b: box [mscorlib]System
Int
IL_
: call string [mscorlib]System
String::Concat(object
object)
IL_
: stloc
IL_
: ldloc
IL_
: call void [mscorlib]System
Console::WriteLine(string)
IL_
c: nop
IL_
d: ret
可以清晰地看到
這兩句C#代碼不僅導致了String類的Concat()方法被調用(IL_
)
而且還引發了裝箱操作(IL_
b)!
Concat()方法會導致CLR為新字串分配內存空間
而裝箱操作不僅要分配內存
還需要創建一個匿名對象
對象創建之後還必須有一個數據復制的過程
代價不菲!
改為以下代碼
String str =
+
=
;
Console
Write(str);
Console
WriteLine(
);
生成的MSIL指令為
maxstack
locals init ([
] string str)
IL_
: nop
IL_
: ldstr
+
=
IL_
: stloc
IL_
: ldloc
IL_
: call void [mscorlib]System
Console::Write(string)
IL_
d: nop
IL_
e: ldc
i
xc
IL_
: call void [mscorlib]System
Console::WriteLine(int
)
IL_
: nop
IL_
: ret
可以看到雖然多了一次方法調用(ConsoleWrite)方法但卻避免了復雜的裝箱操作也避免了調用StringConcat()方法對內存的頻繁分配操作性能更好
.在循環中使用StringBuilder代替String實現字串連接
在某些場合需要動態地將多個子串連接成一個大字串比如許多復雜的SQL命令都是通過循環語句生成的這時應避免使用String類的加法運算符舉個簡單的實例
String str =
;
for (int i =
; i <=
; i++)
{
str += i;
if(i<
)
str +=
+
;
}
上述代碼將生成一個字串
+
+…+
有了前面的知識
讀者一定知道這將導致進行
次裝箱操作
次字串內存分配操作(由String
Concat()方法引發)
由於生成的MSIL指令太長
此處不再列出
請讀者自行用ildasm
exe工具查看上述代碼生成的MSIL指令
改為以下代碼
程序性能會好很多
//預先分配
K的內存空間
StringBuilder sb = new StringBuilder(
);
for (int i =
; i <=
; i++)
{
sb
Append(i);
if(i<
)
sb
Append(
+
);
}
String result = sb
ToString();
通過使用ildasm
exe工具查看生成的MSIL代碼
發現雖然上述代碼生成的MSIL指令比前面多了
條
但卻避免了耗時的裝箱操作
而且內存分配的次數也少了很多
當循環的次數很大時
兩段代碼的運行性能差異很大
[] []
From:http://tw.wingwit.com/Article/program/net/201311/15506.html