CLR支持兩種類型:引用類型和值類型。
雖然FCL中大多數(shù)都是引用類型,但開發(fā)人員用的最多的還是值類型。引用類型總是在托管堆上分配的,C#的new操作符會返回對象的內(nèi)存地址——也就是指向?qū)ο髷?shù)據(jù)的內(nèi)存地址。 使用引用類型必須注意到一些性能問題,首先考慮一下事實: 1)內(nèi)存必須從托管堆上分配。 2)對上分配的每個對象都有一些額外的成員(比如前面提到過得"類型對象指針"和"同步塊索引"),這些成員必須初始化。 3)對象中的其他字節(jié)(為字段而設(shè))總是設(shè)為零。 4)從托管堆上分配一個對象時,可能強制執(zhí)行一次垃圾回收操作。 如果所有類型都是引用類型,應(yīng)用程序的性能會顯著下降。為了提升簡單的、常用的類型的性能,CLR提供了名為"值類型"的輕量型類型。 值類型的實例一般在線程棧上分配的(雖然也可作為字段嵌入一個引用類型的對象中)。在代表值類型的實例的一個變量中,并不包含一個指向?qū)嵗闹羔槨O喾矗兞恐邪藢嵗旧淼淖侄巍! ∮捎谧兞恳呀?jīng)包含了實例的字段,所以為了操作實例中的字段,不再需要提供一個指針。值類型的實例不受垃圾回收器的控制。因此,值類型的使用緩解了托管堆中的壓力,并減少了一個應(yīng)用程序在其生存期內(nèi)需要進(jìn)行的垃圾回收次數(shù)。 .NET Framework SDK文檔明確指出,在查看一個類型時,任何稱為"類"的類型都是引用類型。如System.Exception類、System.Random類等引用類型。文檔將所有值類型都成為結(jié)構(gòu)或枚舉。如System.Int32結(jié)構(gòu)、System.Boolean結(jié)構(gòu)等值類型。 所有值類型都必須從System.ValueType派生。所有枚舉類型都從System.Enum抽象類派生,而System.Enum又是從System.ValueType派生的。CLR和所有編程語言都給予枚舉特殊待遇,以后會提到。 所有值類型都是隱式密封的(sealed),目的是防止將一個值類型用于其他任何引用類型或值類型的基類型。 在托管代碼中,要由定義類型的開發(fā)人員決定在什么地方分配類型的實例,使用該類型的人對此并無控制權(quán)。 以下演示引用類型和值類型的區(qū)別://引用類型class SomeRef { public Int32 x; } //值類型struct SomeVal { public Int32 x; } static void Main(string[] args) { 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; v1.x = 9; Console.WriteLine(r1.x); //8 Console.WriteLine(r2.x); //8 Console.WriteLine(v1.x); //9 Console.WriteLine(v2.x); //5 } 除非以下條件都能滿足,否則不應(yīng)該將一個類型聲明成值類型:
1)類型具有基元類型的行為。 2)類型不需要從其他任何類型繼承 3)類型也不會派生出其他類型。 類型實例的大小應(yīng)該在考慮之列,因為默認(rèn)情況下,實參是以傳值方式傳遞的,這會造成對值類型實例中的字段進(jìn)行復(fù)制,從而影響性性能。同樣的,被定義為返回一個值類型的一個方法在返回時,實例中的字段會賦值到調(diào)用者分配的內(nèi)存中,從而影響性能。 所以,選用值類型還應(yīng)滿足: 1)類型的實例較小(約16字節(jié)或者更小) 2)類型的實例較大(大于16字節(jié)),但不作為方法的實參傳遞,也不從方法返回。 值類型的主要優(yōu)勢在于它們不作為對象在托管堆上分配。 值類型和引用類型的區(qū)別: 1)值類型對象有兩種表示形式:未裝箱(unboxed)和已裝箱(boxed)。引用類型總是處于已裝箱形式。 2)值類型是從System.ValueType派生的。該類型提供了與System.Object定義的相同的方法。然而,System.ValueType重寫了Equals方法和GetHashCode方法。由于這個默認(rèn)實現(xiàn)存在性能問題,所以定義自己的值類型時,應(yīng)該重寫Equals和GetHashCode方法,并提供它們的顯示實現(xiàn)。 3)值類型的所有方法都不能是抽象的,而且所有方法都是隱式密封(sealed)方法。 4)引用類型的變量包含的是堆上的一個對象的地址。默認(rèn)情況,在創(chuàng)建一個引用類型的變量時,它被初始化為null,表明引用類型的變量當(dāng)前不指向一個有效對象。相反,值類型初始化是,所有的成員都會初始化為0。由于值類型的變量不是指針,所以在訪問一個值類型時,不會拋出NullReferenceException異常。CLR確實提供了一個特殊的特性,能為值類型 添加"可空"標(biāo)識。如"int?" 5) 將一個值類型的變量賦給另一個值類型變量,會執(zhí)行一次逐字段復(fù)制。將引用類型賦給另一個引用類型時,只復(fù)制內(nèi)存地址。 6)由于為裝箱的值類型不再堆上分配,所以一旦定義了該類型的一個實例的方法不再處于活動狀態(tài),為他們分配的內(nèi)存就會被釋放。這意味著值類型的實例在其內(nèi)存被回收時,不會通過Finalize方法接收到一個通知。新聞熱點
疑難解答