天天看點

跟小靜讀CLR via C#(02)-基元類型、引用類型、值類型

編譯器能夠直接支援的資料類型叫做基元類型。例如int, string等。基元類型和.NET架構類庫FCL存在着直接的映射關系。

string和String?

面試的時候曾經被問到過這個問題,C#中的基元類型string實際上對應了System.String(FCL)類型,是以兩者使用的時候沒有什麼不同。

類型轉換

編譯器能夠在基元類型之間進行顯式或隐式轉換。如果轉換是安全的,也就是轉換過程不會造成資料丢失,則可以直接采用隐式轉換。如果是不安全的,則必須采用顯式轉換。

Int32 a=5; Int64 b=a; Int32 c=(Int32)b;

引用類型和值類型的差別:

引用類型

值類型

從托管堆中配置設定

從線程的堆棧中配置設定

對象考慮垃圾回收機制

不考慮垃圾回收機制

所有類都是引用

結構或枚舉都是值類型

繼承自System.ValueType

隻有裝箱形式

有兩種形式:裝箱和未裝箱

可以繼承和派生

不能作為基類,不能有虛方法

引用類型變量初始化時預設為null

初始化時預設為0值

複制時隻拷貝記憶體位址

複制時“字段對字段”的拷貝

結構體直接繼承自System.ValueType;而枚舉直接繼承自System.Enum, Enum類又直接繼承自System.ValueType。

下面通過例子看一下他們的差別:

首先定義類和結構體:

class SomeRef { public Int32 x; } struct SomeVal { public Int32 x; }
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; //隻把指針複制給了r2 SomeVal v2 = v1; // 在棧上配置設定空間,并且将變量内容進行複制 r1.x = 8; // r1指向(也是r2指向)的内容修改 v1.x = 9; // 隻修改v1内容,v2内容不會受影響 Console.WriteLine(r1.x); // "8" Console.WriteLine(r2.x); // "8" Console.WriteLine(v1.x); // "9" Console.WriteLine(v2.x); // "5"

看看下圖的記憶體配置設定情況,就一目了然了。

<a href="http://images.cnblogs.com/cnblogs_com/janes/201107/201107041634175932.png"></a>

1. 裝箱過程?

裝箱:将值類型轉換為引用類型。當我們把值類型參數傳遞給需要引用類型參數的方法時,會自動進行裝箱操作。過程如下:

從托管堆為要生成的引用類型配置設定大小。大小為:值類型執行個體本身的大小+額外空間(方法表指針和SyncBlockIndex)。

将值類型字段拷貝到剛剛配置設定的記憶體中。

傳回托管堆中新配置設定記憶體的位址。也就是指向對象的引用。

2. 拆箱過程?

拆箱:擷取指向對象中包含的值類型部分的指針。一般拆箱之後會進行字段拷貝操作,兩個操作加起來才是真正與裝箱互反的操作。過程如下:

如果引用為Null,則抛出NullReferenceException異常。

如果引用對象不是一個期望值類型的已裝箱對象,會抛出InvalidCastException異常。

傳回一個指向包含在已裝箱對象中值類型部分的指針。

3. 執行個體

拆箱的轉型結果必須是它原來未裝箱時的類型。

public static void Main() { Int32 x = 5; Object o = x; // 裝箱 Int16 y = (Int16) o; // 拆箱,抛出InvalidCastException異常 }

       修正:Int16 z=(Int16)(Int32)o;//拆箱成功

這段代碼進行了幾次裝箱?

Int32 v = 5; // 建立值變量 Object o = v; // 裝箱 v = 123; // Changes the unboxed value to 123 Console.WriteLine(v + ", " + (Int32) o); // Displays "123, 5" ,裝箱兩次

上面的代碼進行了3次裝箱,最後一行中v被裝箱為引用類型,o首先被拆箱然後再裝箱為引用類型。

我們看一下它的IL代碼:

<a href="http://images.cnblogs.com/cnblogs_com/janes/201110/201110211045546354.jpg"></a>

字元串連接配接實際上調用的是Concat方法,該方法的參數要求是object,是以各參數都要轉換成object類型。

優化:

Console.WriteLine(v.ToString()+”,”+o);//裝箱0次

我們要盡量少的進行值類型的裝箱拆箱操作,以提高程式性能。

    本文轉自 陳敬(Cathy) 部落格園部落格,原文連結:http://www.cnblogs.com/janes/archive/2011/07/04/2097540.html,如需轉載請自行聯系原作者

繼續閱讀