1. 主要內容
類型的基本概念
值類型深入
引用類型深入
值類型與引用類型的比較及應用
2. 基本概念
C#中,變量是值還是引用僅取決于其數據類型。
C#的基本數據類型都以平臺無關的方式來定義,C#的預定義類型并沒有內置于語言中,而是內置于.NET Framework中。.NET使用通用類型系統(CTS)定義了可以在中間語言(IL)中使用的預定義數據類型,所有面向.NET的語言都最終被編譯為 IL,即編譯為基于CTS類型的代碼,
通用類型的系統的功能:

例如,在C#中聲明一個int變量時,聲明的實際上是CTS中System.Int32的一個實例。這具有重要的意義:
int i; i = 1; string s; s = i.ToString();
CLR 支持兩種類型:值類型和引用類型,

C#的所有值類型均隱式派生自System.ValueType:
值類型(Value Type),值類型實例通常分配在線程的堆棧(stack)上,并且不包含任何指向實例數據的指針,因為變量本身就包含了其實例數據
C#有以下一些引用類型:
可以看出:
2.1內存深入
2.2.1 內存機制
數據在內存中分配位置取決與該變量的數據類型,上圖可知值類型分配在線程的堆棧上,引用類型則分配在托管堆上,由GC控制回收,以下代碼和圖演示了引用類型和值類型的區別:
private static class ReferenceVsValue { // Reference type (because of 'class') private class SomeRef { public Int32 x; }
// Value type (because of 'struct') private struct SomeVal { public Int32 x; }
public static void Go() { SomeRef r1 = new SomeRef(); //在堆上分配
SomeVal v1 = new SomeVal(); // 在棧上分配 r1.x = 5; // 提領指針
v1.x = 5; // 在棧修改 Console.WriteLine(r1.x); // 顯示”5”
Console.WriteLine(v1.x); //同樣顯示”5” // 下圖左半部分反映了執行以上代碼之后的情形
SomeRef r2 = r1; //只復制引用(指針) SomeVal v2 = v1; // 在棧上分配并且復制成員 r1.x = 8; // r1.x和r2.x都會更改
v1.x = 9; // 只是更改v1.x,不會更改v2.x Console.WriteLine(r1.x); // 顯示 "8" Console.WriteLine(r2.x); // 顯示 "8" Console.WriteLine(v1.x); // 顯示 "9" Console.WriteLine(v2.x); // 顯示 "5" //右半部分反映了在執行所有代碼之后的情況 } } 圖5-1 圖解代碼執行時的內存分配情況

SomeVal是用Struct來聲明的,而不是用常用的Class,在C#中用Struct聲明的是值類型,每個變量或者程序都有自己的堆棧,不同的變量不能公用一個內存地址因此上圖中SomeRef和SomeVal一定占用了不同的堆棧,變量經過傳遞后,對v1變量改變時,顯然不會影響到v2的數據,可以看出,堆棧中的v1,v2包含其實際數據,而r1,r2則在堆棧中保存了其實例數據的引用地址,實際的數據保存在托管堆中,因此就有可能不同變量保存了 同一地址的數據引用,當從一個引用類型變量傳遞到另外一個相同的引用類型變量時,傳遞的是引用地址而不是實際的數據,所以改變一個變量的值會影響到另外一個變量的值,值類型與引用類型在內存中的分配是決定其應用不同的根本原因,由此可以容易的解釋為什么傳遞參數的時候,按值傳遞不會改變形參的值,而按地址傳遞會改變形參的值。
內存分配的幾點:
值類型變量做為局部變量時,該實例將被創建在堆棧上;而如果值類型變量作為類型的成員變量時,它將作為類型實例數據的一部分,同該類型的其他字段都保存在托管堆上,將在接下來的嵌套結構部分來詳細說明問題。
引用類型變量數據保存在托管堆上,但是根據實例的大小有所區別,如下:如果實例的大小小于85000Byte時,則該實例將創建在GC堆上;而當實例大小大于等于85000byte時,則該實例創建在LOH(Large Object Heap)堆上。
2.2.2嵌套類型
嵌套結構就是在值類型中嵌套定義了引用類型,或者在引用類型變量中嵌套定義了值類型
public class NestedValueinRef { //aInt做為引用類型的一部分將分配在托管堆上 private int aInt; public NestedValueinRef { //aChar則分配在該段代碼的線程棧上 char achar = 'a'; } } 圖5-2 內存分配圖可以表示為:

引用類型嵌套在值類型時,內存的分配情況為:該引用類型將作為值類型的成員變量,堆棧上將保存該成員的引用,而成員的實際數據還是保存在托管堆中.
public struct NestedRefinValue { public MyClass myClass; public NestedRefinValue { myClass.X = 1; myClass.Y = 2; } }
圖5-3 內存分配圖可以表示為:

新聞熱點
疑難解答