String:一個最熟悉的陌生類型

            .Net 框架程序設計(修訂版)中有這樣一段描述:String類型直接繼承自Object,這使得它成爲一個引用類型,也就是說線程上的堆棧上不會駐留有任何字符串。(譯註:注意這裏的“直接繼承”。直接繼承自Object的類型一定是引用類型,因爲所有的值類型都繼承自System.ValueType。值得指出的是System.ValueType卻是一個引用類型。)。

代碼一:

string str1 = "string";
string str2 = "string";
Console.WriteLine(string.ReferenceEquals(str1, str2));

既然String類型是引用類型,那麼代碼一輸出的應該是False,然而事實上代碼一輸出時的是True。這是因爲當CLR初始化的時,它會創建一個內部的散列表,Key爲字符串,Value爲指向託管堆中字符串對象的引用。當構造str1時,先會去散列表中查詢是否存在”string”字符串,如果不存在那麼會在託管堆中構造一個新的String對象,然後將”string”字符串和指向該對象的引用添加到散列表中,當構造str2時,由於散列表中存在Key爲”string”的引用,於是將Value值賦值給str2,那麼str1和str2引用的是同一個String對象,代碼一自然就返回True了。

代碼二:

static void Main(string[] args)
{
    string str = "string";
    Change(str);
    Console.WriteLine(str);
}
static void Change(string str)
{
    str = "Changed";
}

方法傳遞的參數是原內容的拷貝,其過程如果用圖可表示爲:

語句str=”Changed”之前

 

語句str=”Changed”之後

 

這樣可以看到原來String對象並未改變str=”Changed”只是創建一個新的String對象(其它引用類型是改變內存地址1指向的值),因此這個方法的參數需要加上ref或者out修飾符。因此這裏也可以得出字符串具有恆等性,也就是說一個字符串一旦被創建,我們就不能再將其變長、變短、或者改變其中的任何字符。

 

代碼三:

string str1 = "string";
string str2 = "system." + "string";
string str3 = "system." + str1;
Console.WriteLine(string.Equals(str3, str2));
Console.WriteLine(string.ReferenceEquals(str2, str3));
string str4 = "system.string";
Console.WriteLine(string.Equals(str4, str2));
Console.WriteLine(string.ReferenceEquals(str2, str4));

根據代碼一和二的分析,代碼三的輸出結果爲:True True True True,然而事實卻不是這樣,正確的結果爲:True False True True。這是因爲動態創建的字符串不會去查詢散列表,而是直接在託管堆中創建新的String對象,如語句string str3 = “syetem.”+str1,因此用string.ReferenceEquals來比較str2和str3會返回False,而用string.ReferenceEquals來比較str2和str4會返回True。當然可以將str3字符串手動加入到散列表中,並返回引用:str3 = string.Intern(str3),這樣用string.ReferenceEquals來比較str2和str3會返回True,至於string.Equals都返回True的原因是String重寫了Equals方法,內部會先檢查兩個引用是否指向同一個對象,如果是返回True,不是則再比較各個字符。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章