這篇文章我來總結一些string相關知識。
System.String類型:平時在編程中對於string的用法應該是特別頻繁的。通常我們會把string,int ,float放在一起比較,由於後面兩個都是實值型別,所以非常想當然的會認為string也會是實值型別,這是錯誤的,string由於自身特殊的原因,它最終屬於參考型別,當然它的最終基類還是Object。字串存放在託管堆上,並不存放在堆棧中。
構造字串:如何來構造一個字串呢?
第一:既然string屬於參考型別,那麼是否可以用new操作符來產生呢?下面的代碼是錯誤的。
String c = new String("aaa");
第二:我們通常的做法是不用new,而是直接賦值。
string a = "aaaa";
產生的部分IL代碼:在產生的IL代碼中並沒有出現newobj操作符,但出現了ldstr:推送對中繼資料中儲存的字串的新對象引用。
IL_0001: ldstr "aaaa"
IL_0006: stloc.0
字串駐留池(String Pooling):公用語言運行庫通過維護一個雜湊表來存放字串,該表稱為字串駐留池。它包含程式中以編程方式聲明或建立的每個唯一的字串的一個引用。因此,具有特定值的字串的執行個體在系統中只有一個。 [應該是應用了單例模式的應用。單例可以參考:老生常談:單件模式],這裡非常謝謝飛林沙的提示,應該是享元模式,同樣可以參考如下文章:老生常談:享元模式
字串駐留:根據字串駐留池的定義可以看出字串駐留技術的用處。主要目的是減少記憶體開銷和系統開銷,充分利用資源。提供了兩個重要的方法:
1:String.Intern :檢索系統對指定 String 的引用。如果 str 的值已經留用,則返回系統的引用;否則返回對帶有 str 值的字串的新引用。
2:String.IsInterned 方法 :檢索對指定 String 的引用。如果 str 位於公用語言運行庫“拘留池”中,則為對它的 String 引用;否則為 nullNothingnullptrnull 引用
字串的不可更改特性:當一個字串建立後,我們就不能對它進行增加長,或者是縮減,也不能修改其中的任何字元。但有朋友可以會問,那為什麼能對字串進行那麼多的操作呢?例如:"+"操作符,Substring等等。其實這些操作都會產生一個全新的字串,而不是在原來字串在修改。只要是把操作後的資料賦值給一個新的字串變數,那麼就是產生一個和原字串完全不同的對象。先看下賦值的情況:把一個變數直接賦值給一個新的字串變數,根據上面的理論來看,下面應該會返回false,但實際結果返回的是true,因為在字串駐留的存在。
string b = "aaa";
string c = b;
Console.WriteLine(Object.ReferenceEquals(b, c));
但是這並不代表b和c是一樣的,我們再看下如下代碼:分別分返回aaa,aaaa,false,既然string屬於參考型別,但為什麼修改c的值後,b的值沒有跟著變呢,這隻因為把字串賦值給另一個字串時,會建立一個全新的String對象。
string b = "aaa";
string c = b;
c = b + "a";
Console.WriteLine(b);
Console.WriteLine(c);
Console.WriteLine(Object.ReferenceEquals(b, c));
字串中的"+"操作符:通常我們會對字串進行修改,例如"+"號,表示串連多個字串。但加號會產生非常多的字串,這樣會造成非常多的記憶體開銷和系統開銷,這裡可以利用StringBuilder來解決多字串串連的效能問題。來看下下面的範例程式碼:
string b = "aaa";
b = b + "a";
StringBuilder strb = new StringBuilder();
strb.Append("aaa");
strb.Append("a");
IL代碼:由於ConCat操作後會產生全新的對象,而StringBuild是在原對象上修改變數,並不會產生新對象,這樣就會產生效能優勢。上面的代碼只是示範,一般在大量字串拼接時這種效能問題會倍增。
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// 代碼大小 50 (0x32)
.maxstack 2
.locals init ([0] string b,
[1] class [mscorlib]System.Text.StringBuilder strb)
IL_0000: nop
IL_0001: ldstr "aaa"
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldstr "a"
IL_000d: call string [mscorlib]System.String::Concat(string,
string)
IL_0012: stloc.0
IL_0013: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor()
IL_0018: stloc.1
IL_0019: ldloc.1
IL_001a: ldstr "aaa"
IL_001f: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
IL_0024: pop
IL_0025: ldloc.1
IL_0026: ldstr "a"
IL_002b: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
IL_0030: pop
IL_0031: ret
} // end of method Program::Main
安全字串:如果字串中存放了此比較敏感的資訊,例如使用者的密碼,信用卡資訊之類的,如果此時有非安全的程式碼或者是Unmanaged 程式碼訪問這些字串,這些代碼可以掃描進程的地址空間,訪問包含敏感性資料的字串,以一種非授權的方式來使用這些資料。即使只使用這些字串時間很短,馬上回進行記憶體回收,也是需要一定時間的,而且string本身無法修改的特性,使這些非安全的或者是Unmanaged 程式碼在訪問時會留string的副本在記憶體中。
FCL解決了這個問題,它就是System.Security.SecureString:表示應保密的文本。文本在使用時出於保密目的被加密,並在不再需要時從電腦記憶體中刪除。無法繼承此類。當你構造一個SecureString對象時,系統會在記憶體中分配一個非託管地區,裡麵包含一個字元數組,裡面的內容均是經常加密的。之所以是非託管空間,就是為了不讓記憶體回收行程來管理。
System.Security.SecureString實現了IDisposable介面,這樣可以保證在使用完後立即對字串進行銷毀。