【主要內(nèi)容】
關(guān)于C#的String。
C# String 和 string 有何區(qū)別
字符串的不變性
字符串比較 == 和 Equals(string str1, string str2)
字符串駐留
【概念闡述】
====================
@ C# String 和 string 有何區(qū)別
====================
1、string是c#中的關(guān)鍵字,String是.net Framework的類;
2、c# string映射為.net Framework的String,如果用string,編譯器會(huì)把它編譯成String,所以如果直接用String就可以讓編譯器少做一點(diǎn)點(diǎn)工作;
3、string始終代表System.String,String只有在前面有using System的時(shí)候并且當(dāng)前命名空間中沒有名為String的類型(class、struct、delegate、enum)
的時(shí)候才代表System.String
====================
@字符串的不變性
====================
1、一個(gè)字符串一旦被創(chuàng)建完成后,將不能再做任何改變,比如改變、增加或減少一個(gè)字符。
2、當(dāng)把一個(gè)字符串變量賦給另一個(gè)字符串變量時(shí),會(huì)得到內(nèi)存中同一個(gè)字符串的兩個(gè)引用,但與引用類型在常見的操作上又有區(qū)別。當(dāng)你修改其中一個(gè)字符串的時(shí)候,
會(huì)創(chuàng)建一個(gè)全新的String對(duì)象,其引用的原字符串并不會(huì)改變,因而另一個(gè)字符串變量的值不變。
3、當(dāng)我們創(chuàng)建了字符串對(duì)象a,它的值是“1234”,當(dāng)我們?cè)賱?chuàng)建一個(gè)值為“1234”的字符串對(duì)象b時(shí)它不會(huì)再去分配一塊內(nèi)存空間,而是直接指向了a在內(nèi)存中的地址。
=================================
@字符串比較 == 和 Equals(string str1, string str2)
=================================
==是Equals(string,string)的一個(gè)重載,兩者內(nèi)部機(jī)制完全一樣。如果兩字符串引用相同直接返回true,若不同則先比較引用再比較字符串的值。
【實(shí)驗(yàn)步驟】
=========================
@不可使用new操作符來創(chuàng)建字符串對(duì)象
=========================
編譯錯(cuò)誤提示:
error CS1502: 與“string.String(char*)”最匹配的重載方法具有一些無效參數(shù)
error CS1503: 參數(shù)“1”: 無法從“string”轉(zhuǎn)換為“char*”
事實(shí)上 string 類并未提供 string(string) 構(gòu)造函數(shù)。
==================
@字符串連接測(cè)試(不變性)
==================
輸出:
s1 is a string
s2 is a string
s1 now is another string
s2 now is a string
=============
@ 字符串存儲(chǔ)
=============
1、相同字符串在內(nèi)存中只保存一份。
使用visual studio 2005中的調(diào)試,轉(zhuǎn)到反匯編,可以得到下面內(nèi)容:
......
string a = "1";
0000002b mov eax,dword ptr ds:[022C3048h] // 與下面相同
00000031 mov ebx,eax
string b = "1";
00000033 mov eax,dword ptr ds:[022C3048h] // 與上面相同
00000039 mov esi,eax
string c = "2";
0000003b mov eax,dword ptr ds:[022C307Ch] // 不同
......
2、相同字符串GetHashCode()返回相同值。
輸出:
-842352752
-842352752
-842352753
3、字符串駐留
輸出:
True
說明:當(dāng) CLR 初始化時(shí),它會(huì)創(chuàng)建一個(gè)內(nèi)部的散列表,其中鍵位字符串,值為指向托管堆中字符串對(duì)象的引用,初始化為空。當(dāng)JIT編譯器編譯方法時(shí),
它會(huì)在散列表中查找每一個(gè)常量字符串,并添加到散列表中,值相同的字符串不重復(fù)添加。對(duì)于上面的代碼,編譯器會(huì)對(duì)查找到的第一個(gè)"Hello"字符串,
將現(xiàn)在托管堆中構(gòu)造一個(gè)新的 String 對(duì)象(指向在字符串),然后將 "Hello" 字符串和指向該對(duì)象的引用添加到散列表中。由于散列表中已經(jīng)存在 "Hello"
字符串,對(duì)于查找到的第二個(gè) "Hello" 字符串,將不執(zhí)行任何操作。
代碼執(zhí)行時(shí),它會(huì)在第一行發(fā)現(xiàn)一個(gè) "Hello" 字符串引用,于是便在內(nèi)部散列表中查找 "Hello",找到后將先前創(chuàng)建的 String 對(duì)象的引用保存到變量 s 中。
當(dāng)執(zhí)行第二行代碼時(shí),CLR 會(huì)再一次在內(nèi)部散列表中查找 "Hello",并把對(duì)應(yīng)的 String 對(duì)象的引用傳遞給 Object 的靜態(tài)方法 ReferenceEquals 作為參數(shù)。
通過上面的分析,顯然結(jié)果為 True。當(dāng)一個(gè)引用字符串的方法被 JIT 編譯時(shí),所有嵌入在源代碼中的常量字符串總會(huì)被添加到 CLR 內(nèi)部的散列表中,
但是,運(yùn)行時(shí)動(dòng)態(tài)創(chuàng)建的字符串卻不會(huì),參看下面的代碼:
輸出:
False
說明:在上面的代碼中,s2 引用的字符串 "Hel" 和一個(gè)文本常量字符串 "lo" 連接構(gòu)造一個(gè)新的位于托管堆中的字符串對(duì)象,該引用保存在 s3 中,但并未添加到散列表中,
與散列表中的 "Hello" 在托管堆中各有一份存儲(chǔ),因此返回的結(jié)果是 False。需要注意的是,如果 s3 連接的是兩個(gè)字符串常量 "Hel" + "lo",則又將返回 True。這是因?yàn)?br>C# 編譯器在將代碼編譯成 IL 指令時(shí)會(huì)將兩者連接。
============
@字符串比較
============
輸出:
True
True
說明:參看概念闡述中對(duì)字符串比較內(nèi)部實(shí)現(xiàn)的說明。雖然 s1 和 s3 引用不同,但String 的Equals(string,string) 方法還會(huì)對(duì)字符串的值進(jìn)行比較,因?yàn)橹迪嗤苑祷亟Y(jié)果為 True。顯然用Equals(string,string)/== 效率上要比僅比較引用的 ReferenceEquals(string,string)差很多,如果應(yīng)用程序中所有的字符串比較都僅比較引用,性能將會(huì)大大提升,String 類提供的兩個(gè)靜態(tài)方法允許我們做到這一點(diǎn)。
輸出:
True
True
True
說明:方法 Inter(string) 在 CLR 內(nèi)部散列表中查找參數(shù)指定的字符串,如果能找到返回其引用,如果未找到,字符串將被添加到散列表中,并返回引其引用。
如果 String 對(duì)象不再被進(jìn)程中的所有應(yīng)用程序域所引用(作為參數(shù)傳遞給 Intern 方法的那個(gè) String 對(duì)象),垃圾收集器可以收回其所占內(nèi)存及在三列表中的記錄。
一個(gè)字符串對(duì)象可以被同一個(gè)進(jìn)程中的多個(gè)應(yīng)用程序域訪問,即字符串的駐留是以進(jìn)程為單位的。參看下面的代碼:
輸出:
True
True
False
如果將注釋部分加入到代碼中則輸出:
True
True
True
【參考資源】
《你真的了解.net中的String嗎》 http://terrylee.cnblogs.com/archive/2005/12/26/304876.aspx
《進(jìn)一步了解 String》 http://lixianhuei.cnblogs.com/archive/2005/12/27/305445.html
《Microsoft.NET框架程序設(shè)計(jì)》
聯(lián)系客服