建議1: 正確操作字串
字串應該是所有程式語言中使用最頻繁的一種基礎資料型別。如果使用不慎,我們就會為一次字串的操作所帶來的額外效能開銷而付出代價。本條建議將從兩個方面來**如何規避這類效能開銷:
先來介紹第乙個方面,請看下面的兩行**:
string str1 = "str1
"+ 9
; string str2 = "
str2
"+ 9.tostring();
為了清楚這兩行**的執**況,我們來比較兩者生成的il**。
第一行**對應的il**如下:
.maxstack 8il_0000: ldstr
"str1
"il_0005: ldc.i4.s
9il_0007: box [mscorlib]system.int32
il_000c: call
string [mscorlib]system.string::concat(object, object
) il_0011: pop
il_0012: ret
第二行**對應的il**如下:
.maxstack 2.locals init ([
0] int32 cs$0$0000
) il_0000: ldstr
"str2
"il_0005: ldc.i4.s
9il_0007: stloc.
0il_0008: ldloca.s cs$
0$0000
il_000a: call instance
string
[mscorlib]system.int32::tostring()
il_000f: call
string [mscorlib]system.string::concat(string, string
) il_0014: pop
il_0015: ret
可以看出,第一行**「str1」+ 9在執行時會完成一次裝箱行為(il**中的box);而第二行**中的9.tostring()並沒有發生裝箱行為,它實際呼叫的是整型的tostring方法。tostring方法的原型為:
publicoverride
string tostring()
可能有人會問,是不是原型中的number.formatint32方法會發生裝箱行為呢?實際上,number.formatint32方法是乙個非託管的方法,其原型如下:
[methodimpl(methodimploptions.internalcall), securitycritical]public
static
extern
string formatint32(int value, string format, numberformatinfo info);
它是通過直接操作記憶體來完成從int到string的轉換,效率要比裝箱高很多。所以,在使用其他值引用型別到字串的轉換並完成拼接時,應當避免使用操作符「+」來完成,而應該使用值引用型別提供的tostring方法。
也許有人還會提出疑問:上文所舉的示例中,即使fcl提供的方法沒有發生裝箱行為,但在其他情況下,fcl方法內部會不會含有裝箱的行為呢?答案是:也許會存在。不過,我們這裡有乙個指導原則:
在自己編寫的**中,應當盡可能地避免編寫不必要的裝箱**。
注意 裝箱之所以會帶來效能損耗,因為它需要完成下面三個步驟:
1)首先,會為值型別在託管堆中分配記憶體。除了值型別本身所分配的記憶體外,記憶體總量還要加上型別物件指標和同步塊索引所占用的記憶體。
2)將值型別的值複製到新分配的堆記憶體中。
3)返回已經成為引用型別的物件的位址。
第二個方面:避免分配額外的記憶體空間。對clr來說,string物件(字串物件)是個很特殊的物件,它一旦被賦值就不可改變。在執行時呼叫 system.string 類中的任何方法或進行任何運算(如「=」賦值、「+」拼接等),都會在記憶體中建立乙個新的字串物件,這也意味著要為該新物件分配新的記憶體空間。像下面的 **就會帶來執行時的額外開銷。
privatestatic
void
newmethod1()
private
static
void
newmethod6()
而在以下**中,字串不會在執行時拼接字串,而是會在編譯時直接生成乙個字串。
privatestatic
void
newmethod2()
private
static
void
newmethod9()
由於使用 system.string 類會在某些場合帶來明顯的效能損耗,所以微軟另外提供了乙個型別stringbuilder來彌補string的不足。
stringbuilder並不會重新建立乙個string 物件,它的效率源於預先以非託管的方式分配記憶體。如果stringbuilder 沒有先定義長度,則預設分配的長度為16。當 stringbuilder 字元長度小於等於 16時,stringbuilder 不會重新分配記憶體;當 stringbuilder 字元長度大於16 小於 32時,stringbuilder 又會重新分配記憶體,使之成為 16的倍數。在上面的**中,如果預先判斷字串的長度將大於16,則可以為其設定乙個更加合適的長度(如32)。stringbuilder重新分配內 存時是按照上次的容量加倍進行分配的。當然,我們需要注意,stringbuilder指定的長度要合適,太小了,需要頻繁分配記憶體;太大了,浪費空間。
曾經有人問我,下面的兩種字串拼接方式,哪種效率更高:
1. privatestatic
void
newmethod8()
2. private
static
void
newmethod7()
答案是:兩者效率都不高。不要以為前者比後者建立的字串物件更少,事實上,兩者建立的字串物件相等,且前者進行了3次string.contact方法呼叫,比後者還多了兩次。
要完成這樣的執行時字串拼接(注意:是執行時),更佳的做法是使用stringbuilder型別,**如下所示:
privatestatic
void
newmethod10()
微軟還提供了另外乙個方法來簡化這種操作,即使用string.format方法。string.format方法在內部使用stringbuilder進行字串的格式化,如下面的**所示:
privatestatic
void
newmethod11()
", a, b, c, d);
}
**:《編寫高質量**改善c#程式的157個建議》陸敏技
建議1 正確操作字串
字串是我們常用的基礎資料型別,如果使用不當,養成不好的習慣,會對程式造成額外的開銷。建議 確保盡量少的裝箱 避免分配額外的記憶體空間 確保盡量少的裝箱 1 string str1 str1 9 2 string str2 str2 9.tostring 第一行 需要執行一次裝箱行為int 轉 obj...
字串操作 1
a 大串中查詢子串出現的次數 public class substringdemo 方式2 不擷取字串,逐漸縮小查詢範圍 private static intgetcount2 string str,string regex return count 方式1 查詢到小串後將已經查詢的部分擷取掉。返回...
C 中如何正確的操作字串?
字串應該是所有程式語言中使用最頻繁的一種基礎資料型別。如果使用不慎,我們就會為一次字串的操作所帶來的額外效能開銷而付出代價。本條建議將從兩個方面來 如何規避這類效能開銷 1.確保盡量少的裝箱 2.避免分配額外的記憶體空間。第乙個方面 確保盡量少的裝箱 對於裝拆箱,我們應該不陌生,值型別轉換成引用型別...