本文隻是個人總結見解,勿噴
首先肯定的是string是引用類型
string s_a = "yhc";
string s_b = s_a;
if(s_a.Equals(s_b))
Console.WriteLine("相同?");
else
Console.WriteLine("不相同");
輸出是“相同”,讓s_b=s_a,本質是讓s_b指向了yhc在堆上的存儲位置,此時s_a和s_b都指向了yhc在堆上的存儲位置。
理論上說如果修改s_a的值,例如s_a=”wq”,b也會跟着變化,但是實際上不是的,在c#裡面,如果修改其中一個string對象的值比如s_a,系統會為s_a再重新申請一塊記憶體用來存放修改後的s_a的值wq,而b依然指向于a曾今的記憶體位置。
這就是為什麼在如果要頻繁對string操作的時候建議用stringbuilder,因為stringbuilder類似c++中的标準庫string,是可以動态增加記憶體大小的,而c#中的string是固定大小,你string一個對象就給你配置設定一個固定大小的記憶體空間,這個對象一旦建立就無法更改,如果你要往裡面增加内容,我就必須建立一個string對象,把值拷貝進來,然後把之前的string對象删除,頻繁操作string會帶來效率的降低,畢竟建立對象,配置設定記憶體,删除對象這個過程需要耗時耗資源。
我們可以通過比較記憶體位址來斷定這一結論(ReferenceEquals靜态方法用于比較對象的引用是否一緻,而不是簡單的值),如果s_a和s_b指向的是同一位址,這裡的比較應該傳回相同
s_a =
"wq";
if (ReferenceEquals(s_a,s_b))
Console.WriteLine("相同");
答案很明顯,會輸出“不相同”,這說明系統為s_a重新配置設定了新位址的記憶體空間,當然你也許會說為什麼不用==,因為string的= =被重寫後,其實比較的還是string的值,(當然如果你一定要用= =也是可以的,隻不過需要裝箱成object類型,讓==不再為string的特例重寫比較,即if(((object)s_a)==( (object)s_b))),但是= =在比較其他對象的時候可不是這樣了,這是string這個引用類型表現出值類型特征的一點,其實還有一個地方也表現了string類型的值類型特征表現,就是在string類型作為參數傳遞的時候,傳遞的是string對象的引用位址,但是在傳遞過去的方法裡面修改string對象的值卻不影響本體,這是因為系統根據傳送過來的位址重新構造配置設定了一個全新的string對象,比如調用方法 void fun(string a);并把s_a傳遞作為參數過來的時候,a會被系統重新配置設定一個位址,而不是指向s_a的位址,這是引用類型的一個特例。比如A是一個普通類,比如
A b=new A();
b.name=”yhc”;
fun(b);
//代碼運作到此,如果輸出,此時輸出a的name值:為wq
void fun(A a)
{
//代碼運作到此,如果輸出,此時輸出a的name值:為yhc
a.name=”wq”;//因為參數傳遞的是b的引用,對a的操作就是對b的操作
}
但是如果是這樣:
a=new
A();
a.name=”wq”;
在執行玩fun(b)後的輸出,就不再是wq了,因為在方法fun内部,我們把a指向了一個建立立在堆上的對象A,這個new對象在離開函數方法後會被GC垃圾回收機制回收,什麼時候來回收就不知道了,當然如果是建立在堆棧上的資源會被立刻釋放,但是關于建立在堆上的對象的GC回收也有特例,就是那些非托管對象,比如什麼SqlConnection資料庫連接配接、檔案句柄、網絡連接配接什麼的,這些對象在方法内部new後,離開方法後不會被GC回收。
上面說的跑遠裡,我們回到之前,當然有人會說用ReferenceEquals來比較不夠深刻和準确性,因為s_a改為wq後,值也發生了變化,用ReferenceEquals比較無法判斷是值不同還是引用不同,于是我們如下修改:按照string修改後系統會自動為其配置設定新的記憶體空間的原理我們把s_a修改成yhc,即雖然修改了,但是值還是保持原來的yhc,隻為了測試這個修改操作有沒有重新配置設定記憶體空間
s_a = "yhc";
我們都以為輸出會是“不相同”,但是,但是結果讓我們失望,系統輸出“相同”,原因是微軟的CLR使用了優化技術,叫做字元串駐留技術,這個技術的原理大家可能都知道,就是CLR初始化的時候會建立一個内部散清單,表為鍵值形式,鍵為字元串,比如這裡的yhc,值為yhc在堆記憶體上存儲的位址,初始化的時候散清單肯定為空了,即時編譯器(JIT)編譯時先去散清單找字元串yhc,第一次找沒找到yhc,其就會在堆上開辟空間來存放yhc,然後把存放的位址和yhc這個字元串存儲在散清單中,然後第二次又去找,就是這裡的修改s_a的值為yhc,發現在散清單的鍵中找到了yhc這個值,于是就不再配置設定記憶體來存放修改後的yhc,而是讓s_a依然指向之前這個yhc在堆上存放的空間,到此也就是說,你修改後的s_a=“yhc”,雖然修改了,但是其值沒有變,還是yhc,是以被編譯器的優化機制優化了,是以這個測試還是測試不出來我們要的效果。
上面測試缺陷的原理我們知道了,但是我們差點忘了我們到底是要測試什麼,我們要測試的是s_a或者s_b修改後,系統為其重新配置設定記憶體空間,我們要比較修改後兩者的空間位址是否一緻,于是總結上面教訓如下操作:
s_a=string.Copy(s_b)
這裡,s_a=string.Copy(s_b),string.Copy方法是建立一個與指定的s_b具有相同值的 System.String 的新執行個體。注意是新執行個體,用這個方法則編譯器不會做上面的字元串駐留技術優化,即此時s_a再次被修改了,修改的值是拷貝了s_b的的值(s_b的值為yhc),即此時s_a的值也是yhc,這時候系統輸出“不相同”了,這就驗證了我們string對象被修改後,系統會為他重新配置設定記憶體空間。
完整測試代碼如下:
測試很簡單,但是深究很多
s_a
= string.Copy(s_b);
Console.WriteLine("相同");
Console.WriteLine("不相同");
Console.Read();