這篇文章我來總結一些string相關知識。
System.String類型:平時在編程中對於string的用法應該是特別頻繁的。通常我們會把string,int ,float放在一起比較,由於後面兩個都是值類型,所以非常想當然的會認爲string也會是值類型,這是錯誤的,string由於自身特殊的原因,它最終屬於引用類型,當然它的最終基類還是Object。字符串存放在託管堆上,並不存放在堆棧中。
構造字符串:如何來構造一個字符串呢?
第一:既然string屬於引用類型,那麼是否可以用new操作符來生成呢?下面的代碼是錯誤的。
第二:我們通常的做法是不用new,而是直接賦值。
生成的部分IL代碼:在生成的IL代碼中並沒有出現newobj操作符,但出現了ldstr:推送對元數據中存儲的字符串的新對象引用。
IL_0006: stloc.0
字符串駐留池(String Pooling):公共語言運行庫通過維護一個哈希表來存放字符串,該表稱爲字符串駐留池。它包含程序中以編程方式聲明或創建的每個唯一的字符串的一個引用。因此,具有特定值的字符串的實例在系統中只有一個。 [應該是應用了單例模式的應用。單例可以參考:老生常談:單件模式],這裏非常謝謝飛林沙的提示,應該是享元模式,同樣可以參考如下文章:老生常談:享元模式
字符串駐留:根據字符串駐留池的定義可以看出字符串駐留技術的用處。主要目的是減少內存開銷和系統開銷,充分利用資源。提供了兩個重要的方法:
1:String.Intern :檢索系統對指定 String 的引用。如果 str 的值已經留用,則返回系統的引用;否則返回對帶有 str 值的字符串的新引用。
2:String.IsInterned 方法 :檢索對指定 String 的引用。如果 str 位於公共語言運行庫“拘留池”中,則爲對它的 String 引用;否則爲 nullNothingnullptrnull 引用
字符串的不可更改特性:當一個字符串創建後,我們就不能對它進行增加長,或者是縮減,也不能修改其中的任何字符。但有朋友可以會問,那爲什麼能對字符串進行那麼多的操作呢?例如:"+"操作符,Substring等等。其實這些操作都會產生一個全新的字符串,而不是在原來字符串在修改。只要是把操作後的數據賦值給一個新的字符串變量,那麼就是產生一個和原字符串完全不同的對象。先看下賦值的情況:把一個變量直接賦值給一個新的字符串變量,根據上面的理論來看,下面應該會返回false,但實際結果返回的是true,因爲在字符串駐留的存在。
string c = b;
Console.WriteLine(Object.ReferenceEquals(b, c));
但是這並不代表b和c是一樣的,我們再看下如下代碼:分別分返回aaa,aaaa,false,既然string屬於引用類型,但爲什麼修改c的值後,b的值沒有跟着變呢,這只因爲把字符串賦值給另一個字符串時,會創建一個全新的String對象。
string c = b;
c = b + "a";
Console.WriteLine(b);
Console.WriteLine(c);
Console.WriteLine(Object.ReferenceEquals(b, c));
字符串中的"+"操作符:通常我們會對字符串進行修改,例如"+"號,表示連接多個字符串。但加號會產生非常多的字符串,這樣會造成非常多的內存開銷和系統開銷,這裏可以利用StringBuilder來解決多字符串連接的性能問題。來看下下面的示例代碼:
b = b + "a";
StringBuilder strb = new StringBuilder();
strb.Append("aaa");
strb.Append("a");
IL代碼:由於ConCat操作後會生成全新的對象,而StringBuild是在原對象上修改變量,並不會生成新對象,這樣就會產生性能優勢。上面的代碼只是示範,一般在大量字符串拼接時這種性能問題會倍增。
{
.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
安全字符串:如果字符串中存放了此比較敏感的信息,例如用戶的密碼,信用卡信息之類的,如果此時有非安全代碼或者是非託管代碼訪問這些字符串,這些代碼可以掃描進程的地址空間,訪問包含敏感數據的字符串,以一種非授權的方式來使用這些數據。即使只使用這些字符串時間很短,馬上回進行垃圾回收,也是需要一定時間的,而且string本身無法修改的特性,使這些非安全的或者是非託管代碼在訪問時會留string的副本在內存中。
FCL解決了這個問題,它就是System.Security.SecureString:表示應保密的文本。文本在使用時出於保密目的被加密,並在不再需要時從計算機內存中刪除。無法繼承此類。當你構造一個SecureString對象時,系統會在內存中分配一個非託管區域,裏面包含一個字符數組,裏面的內容均是經常加密的。之所以是非託管空間,就是爲了不讓垃圾回收器來管理。
System.Security.SecureString實現了IDisposable接口,這樣可以保證在使用完後立即對字符串進行銷燬。