[讀書筆記]C#學習筆記八:StringBuilder與String詳解及參數傳遞問題剖析


[讀書筆記]C#學習筆記八:StringBuilder與String詳解及參數傳遞問題剖析

前言html

上次在公司開會時有同事分享windebug的知識, 拿的是string字符串Concat拼接 而後用while(true){}死循環的Demo來說解.
其中有說起string操做大量字符串效率低下的問題, 恰好本身以前也看過相似的問題, 因而便拿出來記錄一下.
本文內容: 參數傳遞問題剖析, string與stringbuilder詳解java

1,參數傳遞問題剖析
數組

對於C#中的參數傳遞,根據參數的類型能夠分爲四類:微信

  • 值類型參數的按值傳遞jsp

  • 引用類型參數的按值傳遞ide

  • 值類型參數的按引用傳遞post

  • 引用類型參數的按引用傳遞性能

1.1值類型參數的按值傳遞學習

複製代碼

 1 class Program 2 { 3     static void Main(string[] args) 4     { 
 5          6         int addNum = 1; 7         // addNum 就是實參, 8         Add(addNum); 
 9      }10 11     // addnum就是形參,也就是被調用方法中的參數12     private static void Add(int addnum)13     {14         addnum = addnum + 1;15         Console.WriteLine(addnum);16     }17 }

複製代碼

對於值類型的按值傳遞,傳遞的是該值類型實例的一個拷貝,也就是形參此時接受到的是實參的一個副本,被調用方法操做是實參的一個拷貝,因此此時並不影響原來調用方法中的參數值,爲了證實這點,看看下面的代碼和運行結果就明白了:
ui

複製代碼

 1 class Program 2 { 3     static void Main(string[] args) 4     { 
 5         // 1. 值類型按值傳遞狀況 6         Console.WriteLine("按值傳遞的狀況"); 7         int addNum = 1; 8         Add(addNum); 9         Console.WriteLine(addNum);   
10       11         Console.Read();12     }13 14     // 1. 值類型按值傳遞狀況15     private static void Add(int addnum)16     {17         addnum = addnum + 1;18         Console.WriteLine(addnum);19     }20 }

複製代碼

 

運行結果是: 
按值傳遞的狀況
2
1
從結果中能夠看出addNum調用方法以後它的值並無改變,Add 方法的調用只是改變了addNum的副本addnum的值,因此addnum的值修改成2了。具體的分析請看下面的圖:


1.2引用類型參數的按值傳遞
當傳遞的參數是引用類型的時候,傳遞和操做的是指向對象的引用(看到這裏,有些朋友會以爲此時不是傳遞引用嗎?怎麼仍是按值傳遞了?對於這個疑惑,此時確實是按值傳遞,此時傳遞的對象的地址,傳遞地址自己也是傳遞這個地址的值,因此此時仍然是按值傳遞的),此時方法的操做就會改變原來的對象。代碼以下:

複製代碼

 1 class Program 2 { 3     static void Main(string[] args) 4     { 
 5         // 2. 引用類型按值傳遞狀況 6         RefClass refClass = new RefClass(); 7         AddRef(refClass); 8         Console.WriteLine(refClass.addnum); 9     }    
10      // 2. 引用類型按值傳遞狀況11     private static void AddRef(RefClass addnumRef)12     {13         addnumRef.addnum += 1;14         Console.WriteLine(addnumRef.addnum);15     }16 }17 class RefClass18 {19     public int addnum=1;20 }

複製代碼

運行結果爲:
2
2
爲何此時傳遞引用就會修改原來實參中的值呢?對於這點咱們仍是參數在內存中分佈圖來解釋下:

1.3string引用類型參數的按值傳遞的特殊狀況
對於String類型一樣是引用類型,然而對於string類型的按值傳遞時,此時引用類型的按值傳遞卻不會修改實參的值,可能不少朋友對於這點很困惑,下面具體看看下面的代碼:

複製代碼

 1 class Program 2 { 3     static void Main(string[] args) 4     { 
 5           // 3. String引用類型的按值傳遞的特殊狀況 6         string str = "old string"; 7         ChangeStr(str); 8         Console.WriteLine(str); 9         10     }11     12      // 3. String引用類型的按值傳遞的特殊狀況13     private static void ChangeStr(string oldStr)14     {15         oldStr = "New string";16         Console.WriteLine(oldStr);17     }18 }

複製代碼

運行結果爲:

New string
old string
對於爲何原來的值沒有被改變主要是由於string的「不變性」,因此在被調用方法中執行 oldStr="New string"代碼時,此時並不會直接修改oldStr中的"old string"值爲"New string",由於string類型是不變的,不可修改的,此時內存會從新分配一塊內存,而後把這塊內存中的值修改成 「New string」,而後把內存中地址賦值給oldStr變量,因此此時str仍然指向 "old string"字符,而oldStr卻改變了指向,它最後指向了 "New string"字符串。因此運行結果纔會像上面這樣,下面內存分佈圖能夠幫助你更形象地理解文字表述:

1.4按引用傳遞

不論是值類型仍是引用類型,咱們均可以使用ref 或out關鍵字來實現參數的按引用傳遞,然而按引用進行傳遞的時候,須要注意下面兩點:

方法的定義和方法調用都必須同時顯式使用ref或out,不然會出現編譯錯誤

CLR容許經過out 或ref參數來實現方法重載。如:

複製代碼

 1 #region CLR 容許out或ref參數來實現方法重載 2 private static void Add(string str) 3 { 4     Console.WriteLine(str); 5 } 6  7 // 編譯器會認爲下面的方法是另外一個方法,從而實現方法重載 8 private static void Add(ref string str) 9 {10     Console.WriteLine(str);11 }12 #endregion

複製代碼

按引用傳遞能夠解決因爲值傳遞時改變引用副本而不影響引用自己的問題,此時傳遞的是引用的引用(也就是地址的地址),而不是引用的拷貝(副本)。下面就具體看看按引用傳遞的代碼:

複製代碼

 1 class Program 2 { 3     static void Main(string[] args) 4     { 
 5         #region 按引用傳遞 6         Console.WriteLine("按引用傳遞的狀況"); 7         int num = 1; 8         string refStr = "Old string"; 9         ChangeByValue(ref num);10         Console.WriteLine(num);11         changeByRef(ref refStr);12         Console.WriteLine(refStr);13         #endregion 14 15         Console.Read();16     }17 18     #region 按引用傳遞19     // 1. 值類型的按引用傳遞狀況20     private static void ChangeByValue(ref int numValue)21     {22         numValue = 10;23         Console.WriteLine(numValue);24     }25 26     // 2. 引用類型的按引用傳遞狀況27     private static void changeByRef(ref string numRef)28     {29         numRef = "new string";30         Console.WriteLine(numRef);31     }32 33     #endregion34 }

複製代碼

運行結果爲:
按引用傳遞的狀況
10
10
new string
new string
從運行結果能夠看出,此時引用自己的值也被改變了,經過下面一張圖來幫忙你們理解下按引用傳遞的方式:



到這裏參數的傳遞全部內容就介紹完了。總之,對於按值傳遞,不論是值類型仍是引用類型的按值傳遞,都是傳遞實參的一個拷貝,只是值類型時,此時傳遞的是實參實例的一個拷貝(也就是值類型值的一個拷貝),而引用類型時,此時傳遞的實參引用的副本。對於按引用傳遞,傳遞的都是參數地址,也就是實例的指針。

2, string與stringBuilder的內部實現
你們應該知道若是作大量的字符串拼接的話, string的效率明顯是低於stringBuilder的, 至於示例我這裏就不在列出了,下面給出個連接能夠查看下.
我這裏只是從string和stringBuilder源碼提及, 經過源代碼的實現方式來講明stringBuilder爲什麼比string效率高.

StringBuilder vs String+String(String concatenation):
一般狀況下,4~8個字符串之間的鏈接,String+String的效率更高。
答案來自: http://stackoverflow.com/a/1612819
StringBuilder vs String.concat():
若是在編譯期間不能肯定要鏈接的字符串個數,用StringBuilder更合適。
答案來自: http://stackoverflow.com/a/4191142

下面先給出結論:

stringbuilder內部維護一個字符數組。下次追加的字符串,直接佔用空餘的位置。 
若是超出上限。數組增大爲原來的兩倍(兩倍還不夠就直接增大到足夠寬度),而後覆蓋原來的數組.

String是不可改變的。每次使用System.String類中的方法之一時,都要在內存中建立一個新的字符串對象,這就須要爲該新對象分配新的空間。
在須要對字符串執行重複修改的狀況下,與建立新的String對象相關的系統開銷可能會很是昂貴。

那麼下面就看看string和stringBuilder源碼有和區別吧, 我這裏是使用的Reflector查看的:
(1)string

打開Reflector,找到string類

找到Concat方法, 咱們這裏以Concat爲例:

下面咱們在看下FillStringChecked(dest, 0, str0)的實現方式:


因此看到這裏結論就出來了: 當咱們隊字符串進行大量操做的時候, 會產生不少的新的字符串, 這些字符串會大量零碎的佔據着堆空間, 大多都是生存期較短的, 會對gc產生比較大的回收壓力.


(2)stringBuilder

看這個類的話,仍是看一下它的源代碼,以Append吧,從下面這個截圖中看出來幾個有意思的地方。

<1> 原來StringBuilder裏面維護的是一個m_ChunkChars的字符數組。

<2> 若是當前的字符串的length<2,會直接給chunkchars數組複製,length>2的時候看到的是剛纔string類中經典的wstrcpy用法,而

      這個時候ptr指向的是chunkChars[chunkLength]的首地址,而不像string中申請新的內存空間,因此從這裏看,比string大大的節省

    了內存空間。




更多細節內容請看@老趙點滴 大神的博客內容吧:

重談字符串鏈接性能上:http://blog.zhaojie.me/2009/11/string-concat-perf-1-benchmark.html
重談字符串鏈接性能中:http://blog.zhaojie.me/2009/12/string-concat-perf-2-stringbuilder-implementations.html
重談字符串鏈接性能下:http://blog.zhaojie.me/2009/12/string-concat-perf-3-profiling-analysis.html

PS:好了, 到了這裏@Learning Hard的<<C#讀書筆記>> 的讀書筆記就分享完了. 後面開始本身學Asp.Net(之前學的是java, 接觸最多的是jsp, 到了公司開始作.Net), 對於Asp.Net還不是太瞭解, 但願用一段時間能夠掌握這個. 另外空閒時間還在讀<<CLR via C#>>這本書, 很不錯的一本書, 須要慢慢去消化.

口語小貼士:
Be my guest.
請便,別客氣.
Boy will be boys.
本性難移!
Don't beat around the bush.
別拐彎抹角了.
Don't bury your head in the sand.
不要逃避現實了.
Don't get me wrong.
不要誤會我
Don't get on my nerves!
不要攪的我心煩
Don't look wise.
別自做聰明.

分類: C#基礎系列讀書筆記

好文要頂 關注我 收藏該文  

一枝花算不算浪漫

相關文章
相關標籤/搜索