標籤:style blog http io color ar 使用 sp for
1.string與StringBuilder
string 是不可變的,是參考型別繼承與Object(實值型別繼承於ValueType),每次拼接string其實是在託管堆上構造一個新的對象。這樣在反覆的拼接字串的時候就會產生大量的垃圾字串,由GC自動回收,這個時候GC會頻繁的回收垃圾字串,影響效能,所以通常推薦使用StringBuilder來做字串拼接,
那麼string支援字串拼接的意義何在呢?
這裡就得說到字串池這個概念,當我們建立一個字串的時候,會先去字串池中找(雜湊表),如果沒有找到就會建立一個新的字串並儲存到字串池中,當建立的字串已經在字串池中存在的時候,就會去引用。這樣已經建立過的字串就可以共用,節省空間的。因為在程式中,我們會使用大量的字串,假如我們每次出現的字串都在記憶體空間中開闢一塊空間來儲存,就會在記憶體中產生大量相同的字串,也會造成記憶體回收頻繁的執行,影響效能。所以string的字串拼接適合少量的拼接,對於大量的拼接,則推薦使用StringBuilder。
下面說說string的字串拼接與StringBuilder拼接的簡單過程。
string的字串拼接過程:string a=‘A‘; a+=B;
1.開闢足夠大的臨時儲存空間來保證足夠儲存字串A和B。
2.將A複製到臨時區的開始處。
3.將B複製到臨時儲存區的結尾處。
4.釋放a的舊有記憶體。
5.給a分配新記憶體。
6.將臨時儲存空間的字串複製到5中新開闢的記憶體,並將a的引用指向新記憶體。
StringBuilder拼接的過程:StringBuilder是鏈式儲存結構,建構函式初始化StringBuilder執行個體的大小,可儲存的最大容量,當前字串。當新添加的字串大於當前StringBuilder剩餘空間的時候,StringBuilder就會開闢出一塊新的空間(大小=原大小*2),新字串補充滿剩餘記憶體空間後,將剩下的字串放入新開闢的記憶體空間。引用一下網上的一張圖。
2者之間的本質區別在於,string在字串拼接的時候是建立一個新的對象來儲存拼接後的字串,而StringBuilder則是在原StringBuilder對象上開闢新的記憶體空間,是操作原對象而非string的建立新對象。
2.實值型別一定在棧上嗎?
我們都知道實值型別是存放線上程棧上面,但其實只有實值型別作為臨時變數的時候才存放在棧上,作為成員變數,儲存在堆中。
1.參考型別的內部變數,即使是實值型別,也會在參考型別被執行個體是一起存放在堆上,方法內部的實值型別變數不會初始化,在執行方法的時候放在棧上面。
2.對於實值型別的數組,因為數組是參考型別。所以數組內的實值型別也在堆上。
3.閉包,lamda運算式。如下:
Action<int> act = a =>{ Console.WriteLine(a); };
C# 成的IL 會添加一個靜態輔助類,閉包內的局部變數 也會成為輔助類的成員變數,因此,這種實值型別的局部變數也被分配到堆上。
閉包的陷阱:參考 菩提樹下的楊過 :http://www.cnblogs.com/yjmyzz/archive/2009/03/13/1410924.html
using System;using System.Collections.Generic;namespace ConsoleTest{ class Program { static void Main(string[] args) { List<Action> ls = new List<Action>(); for (int i = 0; i < 10; i++) { ls.Add(() => Console.WriteLine(i)); } foreach (Action action in ls) { action(); } System.Console.Read(); } } }
結果:一連輸出了10行完全相同的"10"(可能並沒有按代碼編寫者的"意圖",輸出0到9)
看了連結裡面其實是編譯產生了一個類,類裡面有一個變數i和列印i的方法,然後建立一個Action委託集合將方法賦值,最後遍曆集合執行,在執行的時候i已經加到10了。所以要通過一個內部變數來避免這種陷阱。。
3.雜湊表的實現方法?
c#基礎知識