這篇文章將為大家詳細講解有關(guān)c#之System.String的示例分析,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
創(chuàng)新互聯(lián)公司專注于中大型企業(yè)的成都網(wǎng)站設(shè)計、網(wǎng)站制作和網(wǎng)站改版、網(wǎng)站營銷服務(wù),追求商業(yè)策劃與數(shù)據(jù)分析、創(chuàng)意藝術(shù)與技術(shù)開發(fā)的融合,累計客戶上千,服務(wù)滿意度達97%。幫助廣大客戶順利對接上互聯(lián)網(wǎng)浪潮,準確優(yōu)選出符合自己需要的互聯(lián)網(wǎng)運用,我們將一直專注成都品牌網(wǎng)站建設(shè)和互聯(lián)網(wǎng)程序開發(fā),在前進的路上,與客戶一起成長!
基本概念
string(嚴格來說應(yīng)該是System.String) 類型是我們?nèi)粘oding中用的最多的類型之一。那什么是String呢?^ ~ ^
String是一個不可變的連續(xù)16位的Unicode代碼值的集合,它直接派生自System.Object類型。
與之對應(yīng)的還有一個不常用的安全字符串類型System.Security.SecureString,它會在非托管的內(nèi)存上分配,以便避開GC的黑手。主要用于安全性特高的場景。[具體可查看msdn這里不展開討論了。=>msdn查看詳情
特性
由于String類型直接派生于Object,所以它是引用類型,那就意味著String對象的實例總是存在于堆上。
String具有不變性,也就是說一旦初始化,它的值將永遠不變。
String類型是封閉的,換言之,你的任何類型不能繼承String。
定義字符串實例的關(guān)鍵字string只是System.String 類型的一個映射。
注意事項
關(guān)于字符串中的回車符和換行符一般大家喜歡直接硬編碼‘\r\n',但是不建議這么做,一旦程序遷移到其他平臺,將出現(xiàn)錯誤。相反,推薦使用System.Environment類的NewLine屬性來生成回車符和換行符,可以跨平臺使用的。
常量字符串的拼接和非常量字符串在CLR中行為是不一樣的。具體請查看性能部分。
字符串之前加@符號會改變編譯器的行為,如果加了@符號,編譯器會把String中的轉(zhuǎn)義字符視為正常字符來顯示。也就是我定義的什么內(nèi)容就是什么內(nèi)容,主要在使用文件路徑或者目錄字符串中使用。以下兩個String內(nèi)容的輸出將完全一致。
static void Main(string[] args) { string a = "c:\\temp\\1"; string b = @"c:\temp\1"; Console.WriteLine(a); Console.WriteLine(b); Console.Read(); }
性能
c#的編譯器直接支持String類型,并將定義的常量字符串在編譯期直接存放到模塊的元數(shù)據(jù)中。然后會在運行時直接加載。這也說明String類型的常量在運行時是有特殊待遇的。
由于字符串的不變性,也就意味著多個線程同時操作該字符串不會有任何線程安全的問題。這在某些共享配置的設(shè)計中很有用。
如果程序經(jīng)常會對比重復度比較高的字符串,這會造成性能上的影響,因為對比字符串是要經(jīng)過幾個步驟的。為此CLR引入了一個字符串重用的技術(shù),學名叫做‘字符串留用'。原理就是:CLR會在初始化的時候創(chuàng)建一個內(nèi)部的哈希表,key是字符串,value就是留用字符串在托管堆上的引用。
String類型提供了兩個靜態(tài)方法來操作這個哈希表:
String.Intern
String.IsInterned
具體請查看msdn(https://msdn.microsoft.com/zh-cn/library/system.string.isinterned(v=vs.110).aspx)
但是c#編譯器默認是不開啟字符串留用功能的,因為如果程序大量把字符串留用,應(yīng)用程序總體性能可能會變得更慢。(微軟也是挺糾結(jié)的,程序員TMD的更糾結(jié))
如果我們的程序中有很多個一模一樣值的常量字符串, c#的編譯器會在編譯期間把這些字符串合并為一個并寫入模塊的元數(shù)據(jù)中,然后修改所有引用該字符串的代碼。這也是一種字符串重用技術(shù),學名‘字符串池'。這意味著什么呢?這意味著所有值相同的常量字符串其實引用的是同一個內(nèi)存地址的實例,在相同值非常多的情況下能顯著提高性能和節(jié)省大量內(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
可見實例的值只分配了一次,但是有一點需要說明,字符串僅用于編譯期能確定值的字符串,也就是常量字符串。如果我的程序修改為:
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); } }
運行結(jié)果:
字符串地址= 0x2e3c
字符串地址= 0x2e7c
平時coding避免不了字符串的連接,如果一個頻繁拼接字符串的場景下使用‘+',對程序整體性能和GC影響還是挺大的,為此c#推出了 StringBuilder類型來優(yōu)化字符串的拼接。相對于String類型的不變性來說,StringBuilder更像是可變的字符串類型。它的底層數(shù)據(jù)結(jié)構(gòu)是一個Char的數(shù)組。另外還有容量(默認為16),最大容量(默認為int.MaxValue)等屬性。StringBuilder的優(yōu)勢在于字符總數(shù)未超過‘容量'的時候,底層數(shù)組不會重新分配,這和String每次都重新分配形成最大的對比。如果字符總數(shù)超過‘容量',StringBuilder會自動倍增容量屬性,用一個新的數(shù)組來容納原來的值,原來數(shù)組將會被GC回收。可見如果StringBuilder頻繁的動態(tài)擴容也會損害性能,但是影響可能會比String小的多。 合理的設(shè)置StringBuilder初始容量對程序有很大幫助。測試如下:
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);
運行結(jié)果:
14221
查看GC的情況
Gc執(zhí)行的是如此頻繁。 性能是可想而知的。接著看一下StringBuilder
int count = 100000; Stopwatch sw = new Stopwatch(); sw.Start(); StringBuilder sb = new StringBuilder();//聽說程序員都這樣命名StringBuilder for (int i = 0; i < count; i++) { sb.Append(i.ToString()); } sw.Stop(); Console.WriteLine(sw.ElapsedMilliseconds);
運行結(jié)果:
12
GC情況:
幾乎沒有GC(可能還未達到觸發(fā)GC的臨界點),如果我合理初始化了StringBuilder 容量,生產(chǎn)環(huán)境中結(jié)果差距將會更大。 呵呵 ^ ~ ^
其他
關(guān)于字符串留用和字符串池
一個程序集加載的時候,CLR默認會留用該程序集元數(shù)據(jù)中描述的所有文本常量字符串。由于可能會出現(xiàn)額外的哈希表查找造成的性能下降的現(xiàn)象,所以現(xiàn)在可以禁用這個特性了。
coding中我們平常比較兩個字符串是否相等,那這個過程是怎么樣的呢?
首先判斷字符的數(shù)量是否相等。
CLR逐個對比字符最終確定是否相等。
這個場景是適合字符串留用的。因為不再需要經(jīng)過以上的兩個步驟,直接哈希表拿到value就可以對比確定了。
關(guān)于字符串拼接性能
基于以上所有知識,那是不是StringBuilder拼接字符串性能永遠都高于符號‘+'呢?答案是否定的。
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($@"+用時: {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 用時: {sw.ElapsedMilliseconds}"); Console.Read(); }
運行結(jié)果:
+用時: 553
StringBuilder.Append 用時: 975
符號‘+'最終會調(diào)用String.Concat方法,當同時連接幾個字符串時,并不是每連接一個都分配一次內(nèi)存,而是把幾個字符都作為 String.Concat方法的參數(shù),只分配一次內(nèi)存。所以在拼接的字符串個數(shù)比較少的場景下,String.Concat 性能是略高于StringBuilder.Append。string.Format 方法最終調(diào)用的是StringBuilder,這里不做展開討論了,請自行參考其他文檔。
所以萬事都不是絕對的?。∶總€事物都有適合自己的場景,我們都需要自己去探索。(程序員太累了)
關(guān)于“c#之System.String的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
文章標題:c#之System.String的示例分析
分享地址:http://jinyejixie.com/article18/ipijgp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供移動網(wǎng)站建設(shè)、網(wǎng)站策劃、靜態(tài)網(wǎng)站、品牌網(wǎng)站建設(shè)、微信公眾號、用戶體驗
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)