前言
幾乎任何一個(gè)項(xiàng)目都離不開對(duì)字符串的處理,在C和C++編程中,許多程序的漏洞都是由于字符串緩沖區(qū)溢出造成的。為了避免在C#中出現(xiàn)類似的問題,同時(shí)也為了使用更方便,C#中專門設(shè)置了兩個(gè)字符串處理類:String類和StringBuilder類。
本文主要給大家介紹了關(guān)于c#基礎(chǔ)系列之string的相關(guān)內(nèi)容,分享出來供大家參考學(xué)習(xí),下面話不多說了,來一起看看詳細(xì)的介紹吧
擴(kuò)展閱讀:深入理解值類型和引用類型
基本概念
string(嚴(yán)格來說應(yīng)該是System.String) 類型是我們?nèi)粘oding中用的最多的類型之一。那什么是String呢?^ ~ ^
String是一個(gè)不可變的連續(xù)16位的Unicode代碼值的集合,它直接派生自System.Object類型。
與之對(duì)應(yīng)的還有一個(gè)不常用的安全字符串類型System.Security.SecureString,它會(huì)在非托管的內(nèi)存上分配,以便避開GC的黑手。主要用于安全性特高的場(chǎng)景。[具體可查看msdn這里不展開討論了。=>msdn查看詳情
特性

注意事項(xiàng)
static void Main(string[] args) { string a = "c://temp//1"; string b = @"c:/temp/1"; Console.WriteLine(a); Console.WriteLine(b); Console.Read(); } 性能
String.Intern
String.IsInterned
具體請(qǐng)查看msdn(https://msdn.microsoft.com/zh-cn/library/system.string.isinterned(v=vs.110).aspx)
但是c#編譯器默認(rèn)是不開啟字符串留用功能的,因?yàn)槿绻绦虼罅堪炎址粲茫瑧?yīng)用程序總體性能可能會(huì)變得更慢。(微軟也是挺糾結(jié)的,程序員TMD的更糾結(jié))
如果我們的程序中有很多個(gè)一模一樣值的常量字符串, c#的編譯器會(huì)在編譯期間把這些字符串合并為一個(gè)并寫入模塊的元數(shù)據(jù)中,然后修改所有引用該字符串的代碼。這也是一種字符串重用技術(shù),學(xué)名‘字符串池'。這意味著什么呢?這意味著所有值相同的常量字符串其實(shí)引用的是同一個(gè)內(nèi)存地址的實(shí)例,在相同值非常多的情況下能顯著提高性能和節(jié)省大量?jī)?nèi)存。
string s1 = "hello 大菜";string s2 = "hello 大菜";unsafe{ fixed (char* p = s1) { Console.WriteLine("字符串地址= 0x{0:x}", (int)p); } fixed (char* p = s2) { Console.WriteLine("字符串地址= 0x{0:x}", (int)p); }} 輸出結(jié)果:
字符串地址= 0x80002d84
字符串地址= 0x80002d84
可見實(shí)例的值只分配了一次,但是有一點(diǎn)需要說明,字符串僅用于編譯期能確定值的字符串,也就是常量字符串。如果我的程序修改為:
args = new string[] { "dfasfdsa"};string s1 = "hello 大菜"+ args[0];string s2 = "hello 大菜"+args[0];unsafe{ fixed (char* p = s1) { Console.WriteLine("字符串地址= 0x{0:x}", (int)p); } fixed (char* p = s2) { Console.WriteLine("字符串地址= 0x{0:x}", (int)p); }}運(yùn)行結(jié)果:
字符串地址= 0x2e3c
字符串地址= 0x2e7c
平時(shí)coding避免不了字符串的連接,如果一個(gè)頻繁拼接字符串的場(chǎng)景下使用‘+',對(duì)程序整體性能和GC影響還是挺大的,為此c#推出了 StringBuilder類型來優(yōu)化字符串的拼接。相對(duì)于String類型的不變性來說,StringBuilder更像是可變的字符串類型。它的底層數(shù)據(jù)結(jié)構(gòu)是一個(gè)Char的數(shù)組。另外還有容量(默認(rèn)為16),最大容量(默認(rèn)為int.MaxValue)等屬性。StringBuilder的優(yōu)勢(shì)在于字符總數(shù)未超過‘容量'的時(shí)候,底層數(shù)組不會(huì)重新分配,這和String每次都重新分配形成最大的對(duì)比。如果字符總數(shù)超過‘容量',StringBuilder會(huì)自動(dòng)倍增容量屬性,用一個(gè)新的數(shù)組來容納原來的值,原來數(shù)組將會(huì)被GC回收。可見如果StringBuilder頻繁的動(dòng)態(tài)擴(kuò)容也會(huì)損害性能,但是影響可能會(huì)比String小的多。 合理的設(shè)置StringBuilder初始容量對(duì)程序有很大幫助。測(cè)試如下:
int count = 100000;Stopwatch sw = new Stopwatch();sw.Start();string s = "";for (int i = 0; i < count; i++) { s += i.ToString(); }sw.Stop();Console.WriteLine(sw.ElapsedMilliseconds);運(yùn)行結(jié)果:
14221
查看GC的情況

Gc執(zhí)行的是如此頻繁。 性能是可想而知的。接著看一下StringBuilder
int count = 100000;Stopwatch sw = new Stopwatch();sw.Start(); StringBuilder sb = new StringBuilder();//聽說程序員都這樣命名StringBuilderfor (int i = 0; i < count; i++) { sb.Append(i.ToString());}sw.Stop();Console.WriteLine(sw.ElapsedMilliseconds);運(yùn)行結(jié)果:
12
GC情況:

幾乎沒有GC(可能還未達(dá)到觸發(fā)GC的臨界點(diǎn)),如果我合理初始化了StringBuilder 容量,生產(chǎn)環(huán)境中結(jié)果差距將會(huì)更大。 呵呵 ^ ~ ^
其他
關(guān)于字符串留用和字符串池
一個(gè)程序集加載的時(shí)候,CLR默認(rèn)會(huì)留用該程序集元數(shù)據(jù)中描述的所有文本常量字符串。由于可能會(huì)出現(xiàn)額外的哈希表查找造成的性能下降的現(xiàn)象,所以現(xiàn)在可以禁用這個(gè)特性了。
coding中我們平常比較兩個(gè)字符串是否相等,那這個(gè)過程是怎么樣的呢?
這個(gè)場(chǎng)景是適合字符串留用的。因?yàn)椴辉傩枰?jīng)過以上的兩個(gè)步驟,直接哈希表拿到value就可以對(duì)比確定了。
關(guān)于字符串拼接性能
基于以上所有知識(shí),那是不是StringBuilder拼接字符串性能永遠(yuǎn)都高于符號(hào)‘+'呢?答案是否定的。
static void Main(string[] args) { int count = 10000000; Stopwatch sw = new Stopwatch(); sw.Start(); string str1 = "str1", str2 = "str2", str3 = "str3"; for (int i = 0; i < count; i++) { string s = str1 + str2 + str3; } sw.Stop(); Console.WriteLine($@"+用時(shí): {sw.ElapsedMilliseconds}" ); sw.Reset(); sw.Start(); for (int i = 0; i < count; i++) { StringBuilder sb = new StringBuilder();//聽說程序員都這樣命名StringBuilder sb.Append(str1).Append(str2).Append(str3); } sw.Stop(); Console.WriteLine($@"StringBuilder.Append 用時(shí): {sw.ElapsedMilliseconds}"); Console.Read(); }運(yùn)行結(jié)果:
+用時(shí): 553
StringBuilder.Append 用時(shí): 975
符號(hào)‘+'最終會(huì)調(diào)用String.Concat方法,當(dāng)同時(shí)連接幾個(gè)字符串時(shí),并不是每連接一個(gè)都分配一次內(nèi)存,而是把幾個(gè)字符都作為 String.Concat方法的參數(shù),只分配一次內(nèi)存。所以在拼接的字符串個(gè)數(shù)比較少的場(chǎng)景下,String.Concat 性能是略高于StringBuilder.Append。string.Format 方法最終調(diào)用的是StringBuilder,這里不做展開討論了,請(qǐng)自行參考其他文檔。
所以萬事都不是絕對(duì)的!!每個(gè)事物都有適合自己的場(chǎng)景,我們都需要自己去探索。(程序員太累了)
以上都是非生產(chǎn)環(huán)境測(cè)試結(jié)果,如果錯(cuò)誤,請(qǐng)及時(shí)指正
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)武林網(wǎng)的支持。
新聞熱點(diǎn)
疑難解答
圖片精選