2012-04-08 31 views
1

我和我的朋友正在討論關於Dotnet框架中的字符串,他們是如何引用類型但行爲如值類型(不可變)。我們都知道CLR是內部的字符串,但是我們並沒有真正在這個簡短的討論中得出結論,CLR /框架如何創建和管理字符串。如何在CLR中創建和管理字符串?

例如,在下面的代碼顯然是s1s2是不同的實例,但你可以看到,當我做s2.ToUpper()結果指回s1

public static void Main (string[] args) 
    { 
     string s1 = "HELLO"; 
     string s2 = "hello"; 

     Console.WriteLine (s1.GetHashCode()); //Prints 68624562 
     Console.WriteLine (s2.GetHashCode()); //Prints 99162322 
     Console.WriteLine (s2.ToUpper().GetHashCode()); //Prints 68624562 too! 
    } 

所以,這些問題是關於調用s2.ToUpper()做了CLR創建新的字符串"HELLO",並檢查它已經存在,如果是這樣那麼扔掉新創建的字符串?有人能解釋這裏的魔法嗎?

+1

不同的字符串實例可以(會)返回相同的散列碼(即你的代碼並不表明它們是不同的實例)。 – 2012-04-08 17:45:49

+1

我對此沒有太多的瞭解,但我可以想象CLR只是執行ToUpper(),返回值臨時存儲在堆棧(?)上,並在其上應用GetHashCode()。如果你詢問哈希本身,那一切都OK - GetHashCode()是從字符串/另一個對象的* content *生成的。所以兩個相等字符串的哈希值是相同的。它用於平等測試。 – 2012-04-08 17:48:06

+0

字符串以及其他不可變的引用類型**不會像值類型**那樣操作。永恆不變只會帶走你可以用可變引用類型做的一些事情(主要是通過在一個地方對它們進行變異並檢查另一個地方的變化來觀察它們是同一個對象),但是仍然有很多不同之處。假設'var arr = T [] {obj,obj}',用你選擇的值和引用類型試試'ReferenceEquals(arr [0],arr [1])''。 – delnan 2012-04-08 18:45:11

回答

3

這不是一個驚喜2 GetHashCode()調用給出相同的輸入相同的結果,這就是散列的點...

相反當你這樣做:

Console.WriteLine(Object.ReferenceEquals(s2.ToUpper(), s1)); 

它只是返回false。因此,你真的有兩個string實例,都具有相同的內容。

我認爲你需要刷新你對哈希,哈希碼和平等的知識。

或者你來自Java?也許你得到的印象哈希代碼,因爲Object.getHashCode()的文檔指出反對的參考值是相關的:

儘可能多是合理可行的,由Object類定義的hashCode方法確實會針對不同的對象返回不同的整數。 (這一般是通過將該對象的內部地址轉換成一個整數來實現的,但不是由的JavaTM編程語言不需要這種實現技巧。)

+1

對於'Object.ReferenceEquals()'+1。 – 2012-04-08 17:49:56

+0

是的,你說得對,我應該更多地閱讀哈希。 – 2012-04-08 17:54:21

2

您不能使用GetHashCode()來唯一標識實例。散列碼具有對於具有相同值的兩個不同對象是相同的。否則,它不會用作散列碼。

3

String.GetHashCode()生成一個基於該字符串的內容的散列值。所以相同的字符串生成相同的散列是完全自然的。隱含的是,你不能斷定由ToUpper()返回的字符串引用必須與s1引用匹配。而且,這不會實施起來太昂貴。

您可以通過測試驗證碼驗證:

static void Main(string[] args) { 
     var s1 = "hello"; 
     var s2 = "HELLO"; 
     var s3 = s1.ToUpper(); 
     bool eq = object.ReferenceEquals(s2, s3); 
     System.Diagnostics.Debug.Assert(!eq); 
    } 
1

s2.ToUpper()是剛剛方法調用沒有按不改變s2對象的值(s2是String類型的對象)。它取值爲s2,並返回帶有「HELLO」值(ToUpper()方法的結果)的String類的新實例。 在Main函數的範圍內還有兩個對象s1和s2,它們的值保持不變。

0

要添加,請回答您的其他部分...

如果你還檢查這個Object.ReferenceEquals(s2.ToUpper(), s2)你會發現它也是錯誤的。

字符串是immutable - 意思是在這種情況下ToUpper()返回一個新實例。

所以答案是肯定的,「你好」是新的字符串。

但是,正如其他人所說,GetHashCode()只是一個'散列值' - 它主要用於處理散列和字典時使用「填充桶」的多種算法。

或看到這個鏈接What is the best algorithm for an overridden System.Object.GetHashCode? - 和答案 - 讓你很好的理解哈希算法是如何工作的 - 爲什麼它不是唯一的 - 以及爲什麼它對於具有相同內容的字符串可能是相同的。

或者這一個http://ericlippert.com/2011/02/28/guidelines-and-rules-for-gethashcode/