C#中如何正確的使用字符串String
前言
C#中提供了比較全面的字符串處理方法,很多函數(shù)都進(jìn)行了封裝為我們的編程工作提供了很大的便利。System.String是最常用的字符串操作類(lèi),可以幫助開(kāi)發(fā)者完成絕大部分的字符串操作功能,使用方便。
字符串作為所有編程語(yǔ)言中使用最頻繁的一種基礎(chǔ)數(shù)據(jù)類(lèi)型。如果使用不慎,將會(huì)造成不必要的內(nèi)存開(kāi)銷(xiāo),為此而付出代價(jià)。
而要優(yōu)化此類(lèi)型,從以下兩點(diǎn)入手:
1、盡量少的裝箱
2、避免分配額外的內(nèi)存空間
先從第一點(diǎn)裝箱的操作說(shuō)起,查看如下代碼:
//發(fā)生裝箱的代碼 String boxOperate = "test" + 4.5f;
其中間語(yǔ)言IL代碼為如下:
IL_0000: nop IL_0001: ldstr "test" IL_0006: ldc.r4 4.5 IL_000b: box [mscorlib]System.Single IL_0010: call string [mscorlib]System.String::Concat(object, object) IL_0015: stloc.0 IL_0016: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey() IL_001b: pop IL_001c: ret
不難看出,上述代碼發(fā)生了裝箱的操作(IL代碼中的box).裝箱之所以會(huì)發(fā)生性能損耗,因?yàn)樗瓿扇缦氯齻€(gè)步驟:
1、首先,會(huì)為值類(lèi)型在托管堆中分配內(nèi)存。除了值類(lèi)型本身所分配的內(nèi)存外,內(nèi)存總量還要加上類(lèi)型對(duì)象指針和同步塊索引所占用的內(nèi)存,
2、將值類(lèi)型的值復(fù)制到新分配的堆內(nèi)存中。
3、返回已經(jīng)成為引用類(lèi)型的對(duì)象的地址。
在來(lái)看以下代碼:
//沒(méi)有發(fā)生裝箱的代碼 String boxOperate = "test" + 4.ToString();
其中間IL代碼如下:
IL_0000: nop IL_0001: ldstr "test" IL_0006: ldc.r4 4 IL_000b: stloc.1 IL_000c: ldloca.s 1 IL_000e: call instance string [mscorlib]System.Single::ToString() IL_0013: call string [mscorlib]System.String::Concat(string, string) IL_0018: stloc.0 IL_0019: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey() IL_001e: pop IL_001f: ret
如上,并沒(méi)有發(fā)生任何裝箱操作,但是達(dá)到的結(jié)果卻是我們想要的。原因是 4.ToString() 這行代碼并沒(méi)有發(fā)生裝箱行為,是實(shí)際調(diào)用的是整數(shù)型的ToString()方法,其原型如下:
public override string ToString(){ return Number.FormatInt32(m_value, null, NumberFormat.CurrentInfo); }
可能有人會(huì)問(wèn),是不是原型中的 Number.Format_XXX方法會(huì)發(fā)生裝箱行為呢?實(shí)際上,Number.Format_XXX方法是一個(gè)非托管的方法,其原型如下:
[MethodImpl(MethodImplOptions.InternalCall), SecurityCritical]
public statuc extern string FormatInt32(int value, string format,NumberFormatInfo info);
它是通過(guò)直接操作內(nèi)存來(lái)完成 Int32 到 String 的轉(zhuǎn)換,效率要比裝箱高得多。所以,在使用其他值引用類(lèi)型到字符串得轉(zhuǎn)換比完成拼接時(shí),應(yīng)當(dāng)避免使用操作符 “+” 來(lái)我完成,而應(yīng)該使用值引用類(lèi)型提供得ToString方法。
也許有人會(huì)問(wèn):即使FCL提供得方法沒(méi)有發(fā)生裝箱行為,但在其他情況下,F(xiàn)CL方法內(nèi)部會(huì)不會(huì)含有裝箱的行為?也許會(huì)存在,所以,本人推薦:編寫(xiě)代碼中,應(yīng)當(dāng)盡量避免發(fā)生不必要的裝箱代碼。
第二個(gè)方面:避免分配額外的空間。對(duì)于CLR來(lái)說(shuō),String對(duì)象(字符串對(duì)象)是個(gè)很特殊的對(duì)象,它一旦被賦值就不可改變(在內(nèi)存中)。在運(yùn)行時(shí)調(diào)用System.String類(lèi)中的任何方法或進(jìn)行任何運(yùn)算('=‘賦值,'+‘拼接等),都會(huì)在內(nèi)存中創(chuàng)建一個(gè)新的字符串對(duì)象,這也意味著要為該新對(duì)象分配新的內(nèi)存空間。如以下代碼會(huì)帶來(lái)額外開(kāi)銷(xiāo)。
private static void Test(){ String str1 = "aa"; str1 = str1 + "123" + "345"; //以上代碼創(chuàng)建了3個(gè)String對(duì)象,并執(zhí)行了一次String.Contact方法。 }
而在以下代碼中,字符串不會(huì)在運(yùn)行時(shí)拼接字符串,而是會(huì)在編譯時(shí)直接生成一個(gè)字符串。
private static void Test() { String str= "aa" + "123" + "345";//等效 String str= "aa123345"; } private static void Test2() { const String str = "aa"; String newStr = "123" + str; //因?yàn)閟tr是一個(gè)常量,所以該代碼等效于 String newStr = "123" + “aa”; //最終等效于 String newStr = "123aa”; }
由于使用System.String類(lèi)會(huì)在某些場(chǎng)合帶來(lái)明顯的性能損耗,所以微軟另外提供了一個(gè)類(lèi)型StringBuilder來(lái)彌補(bǔ)String的不足。
StringBuilder并不會(huì)重新創(chuàng)建一個(gè)String對(duì)象,它的效率源于預(yù)先以非托管的方式分配內(nèi)存。如果StringBuilder沒(méi)有先定義長(zhǎng)度,則默認(rèn)分配的長(zhǎng)度為16。當(dāng)StringBuilder的長(zhǎng)度大于16小于32時(shí),StringBuild又會(huì)重新分配內(nèi)存,使之成為16的倍數(shù)。StringBuilder重新分配內(nèi)存時(shí)按照上次的容量加倍進(jìn)行分配的。注意:StringBuilder指定的長(zhǎng)度要合適,太小了,需要頻繁分配內(nèi)存;太大了,浪費(fèi)內(nèi)存空間。
以下是例子舉例:
private static String Test3() { String a = "t"; a += "e"; a += "s"; a += "t"; return a; } private static String Test4() { String a = "t"; String b = "e"; String c = "s"; String d = "t"; return a + b + c + d; } //以上兩種效率都不高效。不要以為前者比后者創(chuàng)建的字符串對(duì)象更少,事實(shí)上,兩者創(chuàng)建的字符串對(duì)象相等 //且前者進(jìn)行了3次的String.Contact方法調(diào)用,比后者還多了兩次。
要完成上圖的運(yùn)行時(shí)的字符串拼接(注意:是運(yùn)行時(shí)),更佳的做法是使用StringBuilder類(lèi)型,代碼如下:
private static String Test5() { String a = "t"; String b = "e"; String c = "s"; String d = "t"; StringBuilder sb = new StringBuilder(a); sb.Append(b); sb.Append(c); sb.Append(d); return sb.ToString(); //因?yàn)檎f(shuō)的是運(yùn)行時(shí),所以沒(méi)必要使用以下代碼 //StringBuilder sb = new StringBuilder("t"); //sb.Append("e"); //sb.Append("s"); //sb.Append("t"); //return sb.ToString(); }
微軟還提供了另外一個(gè)來(lái)簡(jiǎn)化這種操作,即使用String.Format 方法。String.Format方法在內(nèi)部使用StringBuilder 進(jìn)行字符串格式化,如下圖代碼:
private static String Test6() { //為演示,定義4個(gè)變量 String a = "t"; String b = "e"; String c = "s"; String d = "t"; return String.Format("{0}{1}{2}{3}", a, b, c, d); }
總結(jié):
在使用String字符串時(shí),應(yīng)該盡量避免裝箱操作和“+”連接操作。
好了,以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)我們的支持。
上一篇:C#語(yǔ)言MVC框架Aspose.Cells控件導(dǎo)出Excel表數(shù)據(jù)
欄 目:C#教程
下一篇:C#實(shí)現(xiàn)截取驗(yàn)證碼圖片
本文標(biāo)題:C#中如何正確的使用字符串String
本文地址:http://mengdiqiu.com.cn/a1/C_jiaocheng/4983.html
您可能感興趣的文章
- 01-10C#通過(guò)反射獲取當(dāng)前工程中所有窗體并打開(kāi)的方法
- 01-10C#實(shí)現(xiàn)Winform中打開(kāi)網(wǎng)頁(yè)頁(yè)面的方法
- 01-10C#實(shí)現(xiàn)由四周向中心縮小的窗體退出特效
- 01-10Extjs4如何處理后臺(tái)json數(shù)據(jù)中日期和時(shí)間
- 01-10C#中DataGridView常用操作實(shí)例小結(jié)
- 01-10C#編程獲取資源文件中圖片的方法
- 01-10asp.net中XML如何做增刪改查操作
- 01-10C#利用反射技術(shù)實(shí)現(xiàn)去掉按鈕選中時(shí)的邊框效果
- 01-10C#中查找Dictionary中的重復(fù)值的方法
- 01-10C#中TreeView實(shí)現(xiàn)適合兩級(jí)節(jié)點(diǎn)的選中節(jié)點(diǎn)方法


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹(shù)的示例代碼(圣誕
- 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問(wèn)題方法
- 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語(yǔ)言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 01-10C#通過(guò)反射獲取當(dāng)前工程中所有窗體并
- 01-10關(guān)于ASP網(wǎng)頁(yè)無(wú)法打開(kāi)的解決方案
- 01-10WinForm限制窗體不能移到屏幕外的方法
- 01-10WinForm繪制圓角的方法
- 01-10C#實(shí)現(xiàn)txt定位指定行完整實(shí)例
- 01-10WinForm實(shí)現(xiàn)仿視頻 器左下角滾動(dòng)新
- 01-10C#停止線程的方法
- 01-10C#實(shí)現(xiàn)清空回收站的方法
- 01-10C#通過(guò)重寫(xiě)Panel改變邊框顏色與寬度的
- 01-10C#實(shí)現(xiàn)讀取注冊(cè)表監(jiān)控當(dāng)前操作系統(tǒng)已
隨機(jī)閱讀
- 01-10C#中split用法實(shí)例總結(jié)
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 01-10SublimeText編譯C開(kāi)發(fā)環(huán)境設(shè)置
- 01-10delphi制作wav文件的方法
- 01-11Mac OSX 打開(kāi)原生自帶讀寫(xiě)NTFS功能(圖文
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
- 04-02jquery與jsp,用jquery
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子