標籤:style blog http color os ar for sp strong
http://www.cnblogs.com/yank/archive/2011/10/24/2204145.html
http://www.cnblogs.com/zwq194/archive/2012/08/06/2625403.html
關於String為實值型別還是參考型別的討論一直沒有平息,最近一直在研究效能方面的問題,今天再次將此問題進行一次明確。希望能給大家帶來點協助。 如果有錯誤請指出。
來看下面例子:
//實值型別 int a = 1; int b = a; a = 2; Console.WriteLine("a is {0},b is {1}", a, b); //字串 string str1 = "ab"; string str2 = str1; str1 = "abc"; Console.WriteLine("str1 is {0},str2 is {1}", str1, str2); Console.Read();
根據上面的例子:你覺得輸出結果應該是什嗎?
輸出結果:
//結果: //a is 2,b is 1 //str1 is abc,str2 is ab
str2依然是ab,並沒有隨str1的改變而改變。
如果string是參考型別,按理Str1和Str指標都指向同一記憶體位址,如果Str的內容發生改變,Str1應該也會相應變化。
此例子,看著string更像是實值型別。
但是MSDN卻說String是參考型別,
參考型別包括: String
所有數組,即使其元素是實值型別
類類型,如 Form
委託
可參考:http://msdn.microsoft.com/zh-cn/library/t63sy5hs(VS.80).aspx
查看具體引用是否相同
如果Net能夠查看記憶體位址就容易了,但不允許,只能通過間接方法來實現,看下面:
static void TestRefAddress() { String str1 = "abc"; String str2 = "abc"; int a = 1; int b = 1; StringBuilder strb1 = new StringBuilder("abc"); StringBuilder strb2 = new StringBuilder("abc"); Console.WriteLine("Reference equal for string: " + Object.ReferenceEquals(str1, str2)); //結果true Console.WriteLine("Reference equal for int: " + Object.ReferenceEquals(a, b)); //結果false Console.WriteLine("Reference equal for StringBuilder: " + Object.ReferenceEquals(strb1, strb2)); //結果false Console.WriteLine("Value equal for string: " + str1.Equals(str2)); //結果true,類似於實值型別 Console.Read(); }
結果為何出現如此情況,分析如下:
Console.WriteLine("Reference equal for string: " + Object.ReferenceEquals(str1, str2)); //結果true,不同對象,但引用地址相同 Console.WriteLine("Reference equal for int: " + Object.ReferenceEquals(a, b)); //結果false,實值型別裝箱操作造成 Console.WriteLine("Reference equal for StringBuilder: " + Object.ReferenceEquals(strb1, strb2)); //結果false,不同對象,引用地址不同 Console.WriteLine("Value equal for string: " + str1.Equals(str2)); //結果true,類似於實值型別
由第一條結果,可以判定不同的String的,相同的值,其引用地址相同,再由第四條結果,str1.Equals(str2),兩者結合,可得出結論,兩個String,如果賦值為同一個值,在記憶體中只有一個字串存在,兩個引用的地址相同。由此引出String的不變性。
String的不變性
string最為顯著的一個特點就是它具有恒定不變性:我們一旦建立了一個string,在managed heap 上為他分配了一塊連續的記憶體空間,我們將不能以任何方式對這個string進行修改使之變長、變短、改變格式。所有對這個string進行各項操作(比如調用ToUpper獲得大寫格式的string)而返回的string,實際上另一個重新建立的string,其本身並不會產生任何變化。 string 對象稱為不可變的(唯讀),因為一旦建立了該對象,就不能修改該對象的值。有的時候看來似乎修改了,實際是string經過了特殊處理,每次改變值時都會建立一個新的string對象,變數會指向這個新的對象,而原來的還是指向原來的對象,所以不會改變。這也是string效率低下的原因。
String的不變,並非說string不能改變,而是其值不能改變。
在例子中str1="ab",這時在記憶體中就將“ab”存下來,如果再建立字串對象,其值也等於“ab”,str2="ab",則並非再重新分配記憶體空間,而是將之前儲存的“ab”的地址賦給str2的引用,這就能印證例子2中的結果。而當str1="abc"其值發生改變時,這時檢查記憶體,發現不存在此字串,則重新分配記憶體空間,儲存“abc”,並將其地址賦給str1,而str2依然指向“ab”的地址。可以印證例子1中的結果。
結論:
String是參考型別,只是編譯器對其做了特殊處理。
關於討論:http://social.msdn.microsoft.com/Forums/zh-CN/visualcshartzhchs/thread/ce580186-86d9-45f7-b5ff-20302caf1324
關於C#深層拷貝淺層拷貝:
C#中有兩種類型變數,一種是實值型別變數,一種是參考型別變數。【淺拷貝就是表面上拷貝了地址;深拷貝就是拷貝了內容 】 對於實值型別變數,copy是屬於全盤複製; 而對於參考型別變數,一般的copy只是淺copy,相當於只傳遞一個引用指標一樣。 因此對於參考型別變數進行真正copy的時候,也是最費事的。
淺拷貝 只是複製本身,它包含的對象只是複製引用,其中包含的對象就直接拿來用了,這樣產生的問題就是:如果一個被修改了,另一個也跟著變。是這樣的,但是除了string類型外,string類型,本來屬於參考型別,因此在進行複製的時候,採用的是淺拷貝,但當發生變化的時候,系統會自動產生進行深copy。 對象的欄位如果是實值型別,那麼無論是淺拷貝還是深拷貝都是原樣複製。
深拷貝 不僅要複製本身,還要複製他所包含對象。對象的欄位如果是參考型別,那麼淺拷貝是複製引用,深拷貝是複製引用的對象。
.net中String是參考型別還是實值型別 以及 C#深層拷貝淺層拷貝