C#基礎之基本類型

來源:互聯網
上載者:User

本絲花了近半年,終於將《CLR Via C#》這本書看完了(請不要BS本人的看書速度T_T),這確實是一本好書,大大們推薦的果然值得一讀。

雖然很多東西還沒有盡得其要,我常想在自己深刻掌握了某個知識點後再總結分享出來(不知道大家是不是這個心理),但現在我覺得應該在一個人成長的過程中就去做這件事情,所以有了本篇不成文的總結,文中知識點大量來自《CLR Via C#》這本書,在此對作者及翻譯者表示感謝!另文中如有錯誤的地方,歡迎大家指出!

術語解釋:

CLR: 通用語言執行平台(Common Language Runtime)

FCL:Framework類庫(Framework Class Library)

IL: 中繼語言(Intermediate Language)

類型基礎

 CLR要求所有的類型最終都是從System.Object派生的。這符合在物件導向的語言和設計中,一切皆為對象!

,我們可以看到,顯式從System.Object派生的類ExplicitlyDerivedFromObject和沒有顯式指定其父類的類ImplicitlyDerivedFromObject,最終產生的IL中間都是一致的。如果我們沒有指定一個類的父類,編譯器會自動為我們的類加上System.Object父類。

基元類型

編譯器直接支援的資料類型稱為基元類型(Primitive type),每種基元類型都對應FCL中的某一類型。如下表格所示每種基元類型與對應的FCL類型:

我們也可以簡單理解成,編譯器會自動在我們的每個源碼檔案中,加上以下using指令(using在此的作用是給類型起一個別名):

using int = System.Int32;using string = System.String;……

理解了這一點,我們應該能知道在我們的代碼中,寫int還是Int32,用string還是用String本質上是一樣的!從下面兩種不同的寫法產生的IL代碼來看,結果也是符合預計的。

我們還應該知道,C#中的int永遠是代表32位整型,long永遠是64位整型等。這一點和其他程式設計語言可能是不一致的(比如C/C++可能會根據機器平台決定,比如int在16位機器上可能是16位,而在32位機器上可能是32位。對於C/C++本人早已記憶模糊,這裡如果有錯誤,請指出!)。

參考型別、實值型別

CLR支援兩種類型:參考型別實值型別,它們的區別是在記憶體配置方式上的差異:參考型別是從託管堆上分配的;實值型別是線上程棧上分配的。而CLR的記憶體回收是針對託管堆的,因此實值型別不受記憶體回收行程的控制。

在FCL中,所有稱為“結構”(struct)的類型都是實值型別,所有稱為“類”(class)的類型都是參考型別。所有的Struct都直接派生自抽象類別System.ValueType,而System.ValueType直接從System.Object派生。所有的枚舉都直接從System.Enum派生,而後者又派生自System.ValueType,所以枚舉也是實值型別。由於CLR的單繼承規則,所以我們在定義實值型別時,不能指定基底類型,但可以實現介面。同時從產生的IL也可以看出,實值型別是隱式密封的(sealed),也就是說也不能從實值型別派生。

雖然參考型別與實值型別實質只是記憶體配置上的差異,但這種差異會導致兩種類型在行為表現上有著明顯不同,比如下面的例子:

    struct ValType { public int x;}    class RefType { public int x;}    class Program    {        static void Main(string[] args)        {            ValType v1 = new ValType(); //在棧上分配記憶體            RefType r1 = new RefType(); //在堆上分配記憶體            v1.x = 2;            r1.x = 2;            //執行到這裡,記憶體結構請見圖1            Console.WriteLine(v1.x);   //2            Console.WriteLine(r1.x);   //2            ValType v2 = v1;    //在棧上分配記憶體(v2),並把v1棧的內容複寫到v2            RefType r2 = r1;    //把r1的堆地址複製給r2            v2.x = 5;   //只改變v2棧的內容            r2.x = 5;   //由於r2和r1都引用同一個堆上的對象,改變r2也會改變r1            //執行到這裡,記憶體結構請見圖2            Console.WriteLine(v1.x);    //2            Console.WriteLine(r1.x);    //5 注意這裡變成了r2修改後的值            Console.WriteLine(v2.x);    //5            Console.WriteLine(r2.x);    //5            Console.ReadKey();        }    }

首先我們定義一個一實值型別與一個參考型別,內部都只有一個欄位。用new操作符分配記憶體時,實值型別v1的記憶體配置在了線程棧上,參考型別r1的記憶體配置在了託管堆上,在程式運行到第一次WriteLine輸出時,看到的結果是一致的。但接下來聲明兩個新的對象並執行賦值時,這裡的發生的事明顯不同:雖然賦值操作都是拷貝線程棧上變數的內容,但由於實值型別變數v1的棧內容就是ValType類型執行個體本身,而參考型別r1的棧內容是RefType對象執行個體在堆上的地址。所以賦值後的結果就是,v1和v2各儲存了一份ValType類型執行個體,而r1和r2儲存了同一塊堆記憶體的地址。所以改變r2對象導致了r1對象的隨同改變。下面是記憶體:

 

圖1

 

圖2 

雖然實值型別執行個體不需要記憶體回收,但由於實值型別在傳遞時,傳遞的是內容本身,所以並不適合將所一些執行個體較大的類型定義為實值型別。實現上除非滿足以下所有條件,否則不應該將一個型別宣告為實值型別。

  • 沒有更改其欄位的成員,即該類型是不可變的。(建議所有欄位為readonly)
  • 類型不需要從其他任何類型繼承。(實值型別不能選擇基類)
  • 類型也不會派生出其他任何類型。(所有的實值型別都是隱式密封sealed的)
  • 執行個體較小(約<=16Byte)或較大但不作為方法實參傳遞,也不從方法返回。
實值型別的裝箱與拆箱

將實值型別轉換成一個參考型別的過程叫裝箱,整個過程看起來是這樣的:

  1. 在託管堆中分配好記憶體,分配的記憶體量=實值型別的各個欄位所需的記憶體量+所有堆上對象都有的兩個額外成員(類型對象指標和同步塊索引)所需的記憶體量。
  2. 實值型別的欄位複製到新分配的記憶體。
  3. 返回對象的地址。

拆箱僅是擷取一個指標的過程,該指標指向包含在一個對象中的原始實值型別(資料欄位)。雖然拆箱比裝箱代價低,但實際在拆箱之後往往緊接著就是賦值操作(記憶體複製)。顯然裝箱和拆箱/複製會對應用程式的速度與記憶體消耗上產生不利影響,所以應該瞭解到這一點,並盡量避免裝箱和拆箱操作。那麼什麼時候會發生裝箱和拆箱,最直觀的方法就是看產生的IL代碼(IL對應指令是分別是box與unbox),比如下面的例子:

樣本中ArrayList的Add方法參數是Object類型,也就是說一個參考型別(在堆上分配的記憶體),當我們傳遞int類型時,這裡便會將int執行個體裝箱,以返回一個堆上的地址。在將array[0]強制轉型為int時,由於實值型別int的對象是線上程棧上分配的,所以這裡拆箱並緊接著發生賦值(記憶體複製)操作。同時為了對比,我加了參考型別的reference,可以看出參考型別是不會發生裝箱與拆箱的。

那麼如何避免(或減少)裝箱與拆箱:

  • 盡量使用泛型集合。
  • 盡量將裝箱與拆箱操作移到迴圈體之外。
  • 定義一個方法如果可接收參考型別或實值型別時,盡量不要將參數定義為object,可以考慮通過重載定義多個版本或定義泛型方法。
總結

本文只能算是對自己看書的一點小結,分享出來的目的一是希望如果對某些知識理解有誤,能及時得到大家指正;同時如果您感覺這篇博文有一點小价值,那我的第二個目的也就達到了。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.