CLR支持兩種類型:引用類型和值類型。
雖然FCL中大多數都是引用類型,但開發人員用的最多的還是值類型。引用類型總是在托管堆上分配的,C#的new操作符會返回對象的內存地址——也就是指向對象數據的內存地址。 使用引用類型必須注意到一些性能問題,首先考慮一下事實: 1)內存必須從托管堆上分配。 2)對上分配的每個對象都有一些額外的成員(比如前面提到過得"類型對象指針"和"同步塊索引"),這些成員必須初始化。 3)對象中的其他字節(為字段而設)總是設為零。 4)從托管堆上分配一個對象時,可能強制執行一次垃圾回收操作。 如果所有類型都是引用類型,應用程序的性能會顯著下降。為了提升簡單的、常用的類型的性能,CLR提供了名為"值類型"的輕量型類型。 值類型的實例一般在線程棧上分配的(雖然也可作為字段嵌入一個引用類型的對象中)。在代表值類型的實例的一個變量中,並不包含一個執行實例的指針。相反,變量中包含了實例本身的字段。 由於變量已經包含了實例的字段,所以為了操作實例中的字段,不再需要提供一個指針。值類型的實例不受垃圾回收器的控制。因此,值類型的使用緩解了托管堆中的壓力,並減少了一個應用程序在其生存期內需要進行的垃圾回收次數。 .NET Framework SDK文檔明確指出,在查看一個類型時,任何成為"類"的類型都是引用類型。如System.Exception類、System.Random類等引用類型。文檔將所有值類型都成為結構或枚舉。如System.Int32結構、System.Boolean結構等值類型。 所有值類型都必須從System.ValueType派生。所有枚舉類型都從System.Enum抽象類派生,而System.Enum又是從System.ValueType派生的。CLR和所有編程語言都給予枚舉特殊待遇,以後會提到。 所有值類型都是隱式密封的(sealed),目的是防止將一個值類型用於其他任何引用類型或值類型的基類型。 在托管代碼中,要由定義類型的開發人員決定在什麼地方分配類型的實例,使用該類型的人對此並無控制權。 以下演示引用類型和值類型的區別:Main(= SomeRef(); SomeVal v1 = SomeVal(); r1.x = = Console.WriteLine(v1.x); === = Console.WriteLine(r2.x); Console.WriteLine(v1.x); Console.WriteLine(v2.x); }1)類型具有基元類型的行為。 2)類型不需要從其他任何類型繼承 3)類型也不會派生出其他類型。 類型實例的大小應該在考慮之列,因為默認情況下,實參是以傳值方式傳遞的,這會造成對值類型實例中的字段進行復制,從而影響性性能。同樣的,被定義為返回一個值類型的一個方法在返回時,實例中的字段會賦值到調用者分配的內存中,從而影響性能。 所以,選用值類型還應滿足: 1)類型的實例較小(約16字節或者更小) 2)類型的實例較大(大於16字節),但不作為方法的實參傳遞,也不從方法返回。 值類型的主要優勢在於它們不作為對象在托管堆上分配。 值類型和引用類型的區別: 1)值類型對象有兩種表示形式:未裝箱(unboxed)和已裝箱(boxed)。引用類型總是處於已裝箱形式。 2)值類型是從System.ValueType派生的。該類型提供了與System.Object定義的相同的方法。然而,System.ValueType重寫了Equals方法和GetHashCode方法。由於這個默認實現存在性能問題,所以定義自己的值類型時,應該重寫Equals和GetHashCode方法,並提供它們的顯示實現。 3)值類型的所有方法都不能是抽象的,而且所有方法都是隱式密封(sealed)方法。 4)引用類型的變量包含的是堆上的一個對象的地址。默認情況,在創建一個引用類型的變量時,它被初始化為null,表明引用類型的變量當前不指向一個有效對象。相反,值類型初始化是,所有的成員都會初始化為0。由於值類型的變量不是指針,所以在訪問一個值類型時,不會拋出NullReferenceException異常。CLR確實提供了一個特殊的特性,能為值類型 添加"可空"標識。如"int?" 5) 將一個值類型的變量賦給另一個值類型變量,會執行一次逐字段復制。將引用類型賦給另一個引用類型時,只復制內存地址。 6)由於為裝箱的值類型不再堆上分配,所以一旦定義了該類型的一個實例的方法不再處於活動狀態,為他們分配的內存就會被回收。