国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

深入理解 StringBuilder

2019-11-08 18:36:32
字體:
供稿:網(wǎng)友
public sealed class StringBuilder : ISerializable位于:System.Text命名空間中。StringBuilder僅實(shí)現(xiàn)ISerializable接口,直接派生自O(shè)bject,相對(duì)于String類型其功能不太完善,如ToUpper、SubString、foreach遍歷每個(gè)字符等等,后面介紹如何擴(kuò)展其功能。它是密封類型,不能通過派生它的子類來改變其行為。StringBuilder能夠動(dòng)態(tài)高效的構(gòu)建字符串、修改字符串,不能保證所有實(shí)例成員都是線程安全的,盡管在類型定義中加入了很多線程安全的控制,如果要確保其線程安全,須手工實(shí)現(xiàn)線程同步機(jī)制。StringBuilder內(nèi)部維護(hù)的是一個(gè)String對(duì)象,在String類型所在的程序外部,String表現(xiàn)出不變性,但在String類型的內(nèi)部,定義了一些改變String對(duì)象的方法,聲明為internal,這些方法供StringBuilder等調(diào)用。最大容量(MaxCapacity):默認(rèn)的最大容量等于int.MaxValue。StringBuilder sb = new StringBuilder(); Console.WriteLine(sb.MaxCapacity); // 2147483647MaxCapacity是只讀屬性,其取值范圍為1~int.MaxValue,如果想設(shè)定自己的最大容量,StringBuild提供了一個(gè)構(gòu)造方法:public StringBuilder(int capacity, int maxCapacity)在構(gòu)造StringBuilder對(duì)象時(shí)設(shè)定最大容量值,最大值一旦設(shè)定,將不可改變,如果Append或其它操作使Length大于MaxCapacity將拋出異常。容量(Capacity):返回StringBuilder的當(dāng)前容量,其取值范圍為:0~MaxCapacity,這個(gè)屬性是可讀寫的,若設(shè)置的值小于Length將拋出異常。長(zhǎng)度(Length):返回StringBuilder內(nèi)部維護(hù)的當(dāng)前String對(duì)象的長(zhǎng)度,取值范圍:0~MaxCapacity,可變屬性。int.MaxValue >= MaxCapacity >= Capacity >= Length >=0 (MaxCapacity >= 1)MaxCapacity只是一個(gè)Capacity和Length的范圍約束,Capacity是StringBuilder內(nèi)部字符串實(shí)際分配內(nèi)存的大小,Length是StringBuilder內(nèi)部字符串的有效字符的數(shù)量。Capacity的變化規(guī)律StringBuilder的Capacity屬性的取值范圍是:0~MaxCapacity;默認(rèn)大小為:16。StringBuilder sb = new StringBuilder(); Console.WriteLine(sb.Capacity); // 16當(dāng)以構(gòu)造方法StringBuilder(String str) 創(chuàng)建StringBuilder對(duì)象時(shí),Capacity的值可用下面的偽代碼表示:IF str.Length < 16 THEN sb.Capacity = 16 ELSE     BEGIN     sb.Capacity = 16     WHILE str.Length < sb.Capacity         sb.Capacity *= 2    END示例:StringBuilder sb = new StringBuilder(new string('a', 16)); Console.WriteLine(sb.Capacity); // 16 StringBuilder sb1 = new StringBuilder(new string('a', 18)); Console.WriteLine(sb1.Capacity); // 32Append等擴(kuò)充字符串操作時(shí),如果結(jié)果字符串的長(zhǎng)度大于Capacity,則Capacity加倍;如果加倍后的Capacity還不足以容納結(jié)果字符串,則Capacity的值等于結(jié)果字符串的長(zhǎng)度。StringBuilder sb1 = new StringBuilder(new string('a', 18)); Console.WriteLine(sb1.Capacity);                              // 32 Console.WriteLine(sb1.Append(new string('b', 18)).Capacity);  // 64  (32 * 2) Console.WriteLine(sb1.Append(new string('c', 100)).Capacity); // 136 (result.Length) Console.WriteLine(sb1.Append(new string('d', 100)).Capacity); // 272 (136 * 2)實(shí)現(xiàn)原理:StringBuilder維護(hù)一個(gè)長(zhǎng)度等于Capacity的字符串(可以看作字符數(shù)組),當(dāng)Capacity長(zhǎng)度的字符串不足以容納結(jié)果字符串時(shí),StringBuilder開辟新的長(zhǎng)度為經(jīng)過上面的規(guī)則計(jì)算好的Capacity的內(nèi)存區(qū)域,將原字符串復(fù)制到新的內(nèi)存區(qū)域再進(jìn)行操作,原字符串區(qū)域交給GC回收。因此這里也涉及到內(nèi)存的分配與回收,使用StringBuilder時(shí)最好估算一下所需容量,用這個(gè)容量初始化Capacity,提高性能。StringBuilder內(nèi)字符串的垃圾數(shù)據(jù)字符串在內(nèi)存中是順序存儲(chǔ)的。StringBuilder內(nèi)部字符串的可用長(zhǎng)度是Capacity,有效字符數(shù)是Length。剛剛構(gòu)造StringBuilder后Length到Capacity的范圍,都保留內(nèi)存中的原垃圾數(shù)據(jù)。初始化后顯式增大Capacity大小后,增大的部分內(nèi)存保留原垃圾數(shù)據(jù)。系統(tǒng)自動(dòng)擴(kuò)大容量,Length到Capacity的部分將清空內(nèi)存中原垃圾數(shù)據(jù)為'/0'。設(shè)置StringBuilder長(zhǎng)度時(shí),若新設(shè)置的Lenght小于原Length,字符串將被截?cái)啵翷ength到原Length的部分填充空字符'/0';若新設(shè)置的Lenght大于原Length,原Lenght到新Length的部分填充空字符'/0'。由于字符串在內(nèi)存中是順序存儲(chǔ)的,可用下面的方法查看StringBuilder內(nèi)部字符串內(nèi)存中的數(shù)據(jù):unsafe static void ShowContent(StringBuilder sb) {     fixed(char* ch = sb.ToString())     {         for(int i = 0; i < sb.Capacity; i++)         {             Console.Write((int)ch[i] + " ");         }     } }ToString方法由下面的示例代碼可以看出,每調(diào)用一次ToString(),獲得的String對(duì)象引用都會(huì)變化。static void Main(string[] args) {     StringBuilder sb = new StringBuilder("Hello StringBuilder!");     ShowAddress(sb.ToString()); // 20656316     ShowAddress(sb.ToString()); // 20666276     ShowAddress(sb.ToString()); // 20666372     ShowAddress(sb.ToString()); // 20666468     ShowAddress(sb.ToString()); // 20666564     ShowAddress(sb.ToString()); // 20666660     ShowAddress(sb.ToString()); // 20666756     ShowAddress(sb.ToString()); // 20666852     ShowAddress(sb.ToString()); // 20666948     ShowAddress(sb.ToString()); // 20667044 } public unsafe static void ShowAddress(string s) {     fixed (char* p = s)     {         Console.WriteLine((int)p);     } }為得出原因,Reflector查看,是如下代碼:public override string ToString() {     string stringValue = this.m_StringValue;     if (this.m_currentThread != Thread.InternalGetCurrentThread())     {         return string.InternalCopy(stringValue);     }     if ((2 * stringValue.Length) < stringValue.ArrayLength)     {         return string.InternalCopy(stringValue);     }     stringValue.ClearPostNullChar();     this.m_currentThread = IntPtr.Zero;     return stringValue; }1.看來ToString()方法對(duì)線程安全進(jìn)行控制了,如果不是當(dāng)前線程訪問,返回字符串的拷貝。2.ArrayLength應(yīng)該就是Capacity了,如果長(zhǎng)度小于Capacity的1/2,為優(yōu)化性能,返回字符串的新的拷貝。3.以上條件不滿足,返回StringBuilder內(nèi)部字符串。但調(diào)用一次ToString()后,執(zhí)行了this.m_currentThread = IntPtr.Zero; ,如果再緊接著執(zhí)行ToString()會(huì)返回新的字符串。要返回StringBuilder內(nèi)部字符串的真實(shí)地址,可用反射或序列化取得StringBuilder內(nèi)的字符串的引用,再取字符串的地址,StringBuilder內(nèi)字符串聲明為:internal volatile string m_StringValue;StringBuilder sb = new StringBuilder("Hello StringBuilder!"); ShowAddress(sb.ToString()); // 21075152 ShowAddress(sb.ToString()); // 21117944 ShowAddress(sb.ToString()); // 21118040 ShowAddress(sb.ToString()); // 21075152 SerializationInfo info = new SerializationInfo(     typeof(StringBuilder), new FormatterConverter()); ((ISerializable)sb).GetObjectData(     info, new StreamingContext()); String s = info.GetString("m_StringValue"); ShowAddress(s); // 21075152可見,第一次調(diào)用ToString()方法非常高效,直接返回StringBuilder內(nèi)字符串的引用。如果沒有重新取得m_currentThread,接下來的調(diào)用會(huì)拷貝構(gòu)造新的字符串。CLR會(huì)記錄該StringBuilder維護(hù)的String已被引用,如果試圖對(duì)其修改,StringBuilder會(huì)重新分配內(nèi)存區(qū)域,將原字符串拷貝到新的內(nèi)存區(qū)域然后進(jìn)行修改。ToString重載的另一個(gè)版本原型為:public string ToString(int startIndex, int length)會(huì)構(gòu)造新的字符串,新字符串值為:起始索引為startIndex,長(zhǎng)度為length的子字符串,這個(gè)重載能實(shí)現(xiàn)String中的SubString的功能。EnsureCapacity確保最小的容量不小于給定的數(shù)值。如果給定的數(shù)值值小于目前的Capacity,則忽略給定的數(shù)值;如果給定的數(shù)值大于Capacity,則設(shè)置Capacity為給定的數(shù)值。 AppendFormat有時(shí)感覺StringBuilder連接字符串不如String連接方便,如果用AppendFormat會(huì)方便很多,使用方法跟String.Format相似,這個(gè)方法保證了字符串連接的優(yōu)雅與高效。不知道大家有沒有注意到,Append、AppendFormat、AppendLine、Insert、Remove、Replace等方法對(duì)StringBuild對(duì)象操作完成后都返回StringBuilder自身,這樣的設(shè)計(jì)便于進(jìn)行一連串的操作。如:StringBuilder sb0 = new StringBuilder("abc",10); string s = sb0.Append("abc").     Replace("ca", "--").     Insert(0, "String:").     ToString();擴(kuò)展StringBuilder的操作StringBuilder與String相比,非常多的操作沒有實(shí)現(xiàn),可以調(diào)用ToString()后再進(jìn)行操作,但這樣會(huì)影響效率;也可以用擴(kuò)展方法來擴(kuò)展其操作;既然上面能取得StringBuilder成員m_StringValue的值,可以直接用所有String的方法來處理判斷,但這樣會(huì)造成一些問題,不推薦這樣做。擴(kuò)展StringBuilder,如foreach遍歷所有字符、每個(gè)字符字符轉(zhuǎn)換為其對(duì)應(yīng)的大寫字符示例:static class PRogram {     static void Main(string[] args)     {         StringBuilder sb = new StringBuilder("Hello StringBuilder!");         foreach (var c in sb.GetEnumerator())         {             Console.WriteLine(c);         }         Console.WriteLine(sb.ToUpper());         Console.ReadKey();     }     static IEnumerable<Char> GetEnumerator(this StringBuilder sb)     {         for (var i = 0; i < sb.Length; i++)         {             yield return sb[i];         }     }     static StringBuilder ToUpper(this StringBuilder sb)     {         for (var i = 0; i < sb.Length; i++)         {             sb[i] = Char.ToUpper(sb[i]);         }         return sb;     } }
發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 电白县| 广东省| 潮安县| 河东区| 林甸县| 渝中区| 汝城县| 潜江市| 报价| 伊吾县| 新邵县| 六盘水市| 崇明县| 上高县| 广水市| 清水河县| 游戏| 昌都县| 廉江市| 嘉兴市| 明星| 襄樊市| 台东县| 海口市| 湘西| 永嘉县| 仁怀市| 安丘市| 岳池县| 凤山市| 普兰县| 华容县| 含山县| 鹤壁市| 丰城市| 临江市| 博客| 和田县| 仙游县| 田林县| 乌苏市|