這個問題應該是困擾任何一門語言初學者的問題,為此整理了網上的一些資料,希望能為大家解惑。
首先對於C#中的資料類型,只有兩種,不是實值型別就是參考型別。
實值型別:結構體(數實值型別,bool型,使用者定義的結構體),枚舉,可空類型
參考型別:數組,使用者定義的類、介面、委託,object,字串。
從表面的概念上看,實值型別是直接儲存其值,而參考型別儲存的是對值的引用。
從儲存在記憶體的位置來看,實值型別是儲存在堆棧(線程堆棧)上,而參考型別是儲存在託管堆上。
(事實上,對於新手來說,也沒有必要去細究堆棧和託管堆是什麼,只需要知道是記憶體中的兩塊不同的地區就可以了,但為了保持嚴謹的治學態度,我建議大家可以參考:http://www.cnblogs.com/axzxs2001/archive/2008/08/28/1279037.html
)
講這些內容還是比較晦澀的,下面通過一個樣本講解:
1//參考型別SomeRef
2
3 class SomeRef
4
5 {
6
7 public Int32 x;
8
9 }
10
11 //實值型別SomeVal
12
13 struct SomeVal
14
15 {
16
17 public Int32 x;
18
19 }
20
21 static void ValueTypeDemo()
22
23 {
24
25 SomeRef r1 = new SomeRef();//在託管堆上分配
26
27 SomeVal v1 = new SomeVal();//在堆棧上分配
28
29 r1.x = 5; //提領指標
30
31 v1.x = 5; //在堆棧上實際修改
32
33 Console.WriteLine(r1.x); //顯示5
34
35 Console.WriteLine(v1.x); //顯示5
36
37
38
39
40 //以上代碼為圖的左邊部分,以下代碼為右邊部分
41
42
43
44
45 SomeRef r2 = r1; //只複製引用()
46
47 SomeVal v2 = v1; //在堆棧上分配並複製成員
48
49 r1.x = 8; //修改r1.x和r2.x
50 v1.x = 9; //只修改v1.x而不修改v2.x
51 Console.WriteLine(r1.x); //8
52 Console.WriteLine(r2.x); //8
53 Console.WriteLine(v1.x); //9
54 Console.WriteLine(v2.x); //5
55
56 }
57
58
事實上通過這個樣本,大家已經可以很清晰的區分實值型別與參考型別的一些區別了,尤其關於賦值的問題:實值型別賦值是通過值的複製來實現的,修改新的變數的值對原變數沒有影響,而參考型別的賦值實際上是賦予給對象的引用,修改任何一個的值實際上修改了其引用,所有指向這個引用的變數的值都會發生改變。
但有時候你會發現這樣的問題:
string s1 = "hello";
string s2 = s1;
Console.WriteLine(s1); //hello
Console.WriteLine(s2); //hello
s1="goodbye";
Console.WriteLine(s1); //goodbye
Console.WriteLine(s2); //hello
剛才我們也提到string類型屬於參考型別,又根據參考型別值的特點,這裡修改s1的值後,s2的值應該發生改變才對?
事實上這裡並不矛盾,原因在於當改變s1的值是,實際上新建立了一個string對象,s1引用這個新的string對象;s2仍然引用原來string對象。產生這種行為的原因是string對象是恒定的,也就是說,一旦一個string對象被建立,它的值就不能再修改,所以當改變一個字串變數的值的時候,僅僅是新建立了一個包含修改內容的新的string對象。這點大家要特別注意。
補充:
設計自己的類型時,是考慮使用實值型別還是參考型別,某些時候,實值型別比參考型別有更好的效能。以下幾種情況應該考慮使用實值型別:
(1)類型具有一個基元類型的行為,也就是這是一個非常簡單的類型,其中沒有成員會修改類型的任何欄位。
(2)類型不需要從其它任何類型繼承也不會派生出其它任何類型
(3)類型的執行個體較小,約為16位元組或更小
(4)類型的執行個體較大,大於16位元組,但不作為方法傳遞,也不作為方法的傳回型別使用
參考文章:http://www.cnblogs.com/chenzehe/archive/2009/01/30/1381073.html
http://blog.csdn.net/canduecho/archive/2008/01/13/2042509.aspx