大內高手專欄:
.NET CTS (Common Type System)
作者:蔡學鏞
2004 年 5 月
CTS 非常重要,CTS (Common Type System) 是 .NET CLR 的核心,這樣的說法並不為過。任何 .NET 的程式員都需要對於 CTS 有最基本的認識,也因此本文的目的在於讓大家瞭解 CTS,但是由於 CTS 的定義相當繁瑣,所以本文章只能包含一小部分。請注意,由於 CTS 的定義中有一些地方很容易造成學習上的混淆,所以本文並未完全依照 CTS 的定義來解說。
任何程式語言,如果想要在 .NET CLR 上執行,就必需提供一個編譯器,將此語言的程式編譯成 .NET CLR 所認識的 metadata 以及 IL,符合 CTS 的規定。
並非所有的語言都能和 C# 一樣符合 CTS 的規範,畢竟許多語言出現在先,CTS 出現在後,所以有一些舊的語言未能符合 CTS 的規定。這類的程式語言在 .NET 中有下列三種因應方式:
- 改變語言本身以符合 CTS 的規定。例如 Visual Basic 6 就為此做了大幅度的擴充與改變,加上繼承的特性,這使得新版的 Visual Basic .NET 差不多可以算是一個全新的語言了,猶如當年 C 衍生出 C++ 一樣。
- 擴充語言本身以接近 CTS 的規定,但仍保留不相容於 CTS 的語法,如此一來,程式中符合 CTS 規定者以 CTS 的方式編譯,不符合 CTS 規定者則以傳統的方式編譯成 native code。例如 C++ with Managed Extension (簡稱 MC++) 就是如此。
- 語言本身盡量維持不變,一切都是透過超強的編譯器設計來達成和 CTS 的相容,Eiffel 就是如此的作法。例如,CTS 不支援多重繼承 (multiple inheritance),但是 Eiffel 支援多重繼承,透過 Eiffel 的編譯器可以利用 interface 以及 attribute 來達到多重繼承 (這樣的作法相當巧妙,有興趣的讀者不妨去研究一下)。
我們可以將 CTS 看成是所有 .NET 語言的 superset (union),而符合 CTS 的各種不同的語言,其實都只是 CTS 的 subset (intersection)。這些語言所寫出來的程式,如果想要有最佳的相容性,以便互相調用 (invoke) 或繼承,這些語言之間就必需取得一個共同的 subset,有共同遵守的規範,這就是 CLS (Common Language Specification,或 Common Language Subset)。
圖一是整個概念的示意圖。
.gif)
圖 1:CTS 和其他語言的關係
資料的內容稱為值 (Value),每個值一定屬於某些型別 (Type),而最完整地表達此值的型別稱為 Exact Type (精準型別)。
根據 Value 是否可以再切割,Value 分成兩種:
- Simple Value (簡單值):指的是不可再分割 (atomic) 的資料。
- Complex Value:Complex Object 指的是由多個 Simple Value 所組成的資料。
根據 Value 是否可以直接使用,Type 分成兩類:
- Value Type:直接使用 Value Type 的 Value。
- Reference Type:不能直接使用 Reference Type 的 Value,只能間接存取資料。
Value Type 和 Reference Type 的處置方式很不同:Value Type 是直接存取資料,Reference Type 則是間接存取資料。Reference Type 對程式員來說是最方便的作法,但其實 Value Type 的效率比較好,所以兩者各有適合的地方。請參考圖二。
有些時候,我們需要把 Value Type 當成 Reference Type 來使用,所以我們需要一套機制來將 Value Type 的值裝箱 (box) 成 Reference Type 的值,這套機制就叫做 Boxing。當 Boxed Value 需要再度從 Reference Type 被轉回原來的 Value Type,這套機制就叫做 Unboxing (拆箱)。
.gif)
圖 2:各種值的記憶體配置方式
CTS 對於 Type 有一個很詳細的分類,但是它的分類方式我不喜歡,因為容易造成混淆。所以我對其做了一些修改,我所做的修改包括了:
- 將 Interface 自 Reference Type 中獨立出來,因為 Interface 可以是 Value Type,也可以是 Reference Type。(這點和 Java 不同)
- 將 Pointer 自 Reference Type 中獨立出來,因為 Pointer 其實和 Reference Type 的差異很大。
- 將 Build-in Value Type 之下的 Integer、Floating Point、Typed Reference 刪除,因為這三者未能包含 Boolean 以及 Void。
- 將 Build-in Value Type 改名為 Primitive Type,新名稱乃是遵循 IL Assembly 設計者 Serge Lidin 的稱呼方式。(這也是 Java 慣用的稱呼方式)
- 將 Enumeration 自 User Defined 中獨立出來。將 User Defined 改名為 Structure。這乃是根據繼承的父類別來分類。
- 刪除 Build-in Reference Type 以及其子節點 String 與 Object。因為 String 與 Object 只有在 Signature 中和其他 Class 不同之外,並無特殊之處。我認為不需要特別分成一類。
- 將 Self-Describing 及其子節點大幅更動,因為原來的方式太混亂。更動後的版本等下會看到。
- 將 Box Value Type 及其子節點刪除,因為 Box Value Type 只是一個概念,並非真的存在,其實使用的還是原來的 Type。
- 將 Array 再細分成兩種,分別是 Vector 以及 Multi-Dimensional Array。(請注意,這裡的 Vector 和 Java 的 Vector 是不同的意思)
如果你對於原本的 CTS 分類方式感興趣,請自行參考相關資料,這裡不再列出。下面列出的是我修改後的版本。
如果你對於原本的 CTS 分類方式感興趣,請自行參考相關資料,這裡不再列出。下面列出的是我修改後的版本。
- Value Type
- Structure (Extended From ValueType directly or indirectly)
- Enumeration (Sealed, and Extended From ValueType directly)
- Reference Type
- Regular Class
- Array (Sealed, and Extended From Array directly and implicitly, Don’t have a type name)
- Delegate (Sealed, and Extended From MulticastDelegate directly)
- Interface
- Pointer
- Function Pointer
- Managed Data Pointer
- Unmanaged Data Pointer
後面會一一解說這些型別。圖三是這些型別的繼承架構。
.gif)
圖 3:CTS 的一些重要型別
除了 Interface 以及 Pointer 之外,所有的型別都是直接或間接繼承自 System.Object。這張圖所列出來的每個型別都很特別,對於 CTS 而言都有特殊的意涵。
所有的 Value Type 都直接或間接地繼承自 System.ValueType。若不繼承自 System.ValueType,則一定是 Reference Type。
Enumeration 一定是直接繼承自 System.Enum;而 Structure 一定是直接繼承自 System.ValueType。Structure 型別中有一些很特殊者,在 .NET CLR 中有特殊的處理方式,這些 Structure 被稱為 Primitive Type,詳列於下面的表格:
Code | Type Name | Comments |
0x01 | Void | |
0x02 | Boolean | Single-byte value, true = 1, false = 0 |
0x03 | Char | 2-byte
unsigned integer, presenting a Unicode character |
0x04 | Sbyte | Signed
1-byte integer, the same as char in C/C++ |
0x05 | Byte | Unsigned
1-byte integer |
0x06 | Int16 | Signed
2-byte integer |
0x07 | UInt16 | Unsigned
2-byte integer |
0x08 | Int32 | Signed
4-byte integer |
0x09 | UInt32 | Unsigned
4-byte integer |
0x0A | Int64 | Signed
8-byte integer |
0x0B | UInt64 | Unsigned
8-byte integer |
0x0C | Single | 4-byte
floating-point |
0x0D | Double | 8-byte
floating-point |
0x16 | TypedReference | Typed
reference, carrying both reference to a type and information identifying the
referenced type |
0x18 | IntPtr | Pointer-size integer; size dependent on the underlying platform. |
0x19 | UIntPtr | Pointer-size unsigned integer |
Delegate 是一種物件導向的 Function Pointer (不同於傳統的 Function Pointer)。所有的 Delegate 都必需直接繼承自 System.MulticastDelegate。
任何 Array 都必需直接繼承自 System.Array,但是這樣的繼承關係是「由 .NET CLR 內部自行認定如此」,我們並不會在 .NET PE 檔內部看到這樣的繼承關係。事實上,在 .NET PE 檔內部連 Array 型別 (這裡指的不是 System.Array) 的定義都不可能出現,因為 Array 型別的存在完全是透過 Signature,而非 TypeDef 或 TypeRef 的 Metadata Table (關於 Array 的許多特點,.NET 的作法很類似 Java)。
還有一點值得我們注意的,CTS 支援兩種 Array,分別是:
- Vector:是一維的 Array,且 index 值由 0 開始。
- Multi-Dimension Array:是多維的 Array (當然也可以當一維使用),且 index 值可以不必由 0 開始。
千萬不要輕忽這兩種 Array 的差別,兩者在彈性和效率上各有擅場,你甚至可以組合出許多的變化,例如:利用「Vector 的 Vector」,製作出非矩形的 Array。
任何直接或間接繼承自 System.Object,且未直接或間接繼承自 System.ValueType、System.Delegate、System.Array 者,都是屬於 Regular Class。
Interface 是一種很特殊的型別,Interface 不能當作 Exact Type,所以由其當時的 Exact Type 來決定 Value 所屬的型別是 Value Type 或 Reference Type。
CTS 也支援三種 Pointer,分別是:
- Function Pointer:記憶體位址,指向一個 function 的起點。請注意:C#「不」支援 Function Pointer。
- Managed Data Pointer:記憶體位址,指向一個 managed data。請注意:C#「不」支援 Managed Data Pointer。
- Unmanaged Data Pointer:記憶體位址,指向一個 unmanaged data。請注意:C#「支援」Unmanaged Data Pointer,但不常用,也不鼓勵使用。
Value Type、Reference Type、以及 Interface,可以有兩種不同的 Accessibility (請參考圖四)。分別是:
- Public:開放所有的程式使用此 Type。
- Assembly:也就是 C# 的 Internal,是預定的。僅開放同一個 Assembly 的程式使用此 Type。
.gif)
圖 4:Value Type、Reference Type、以及 Interface 的 Accessibility
如果是 Nested Type (定義在另一個 Type 內部的 Type) 的話,則有比較多種 Accessibility。如下圖五所示:
.gif)
圖 5:Nested Type 的 Accessibility
- Public:開放所有的程式使用此 Type。
- Family-or-Assembly:開放給同一個 Assembly 的程式,或者 Sub-Type (繼承自此 Type 的 Type) 的程式。在 C# 中稱為 Protected Internal (或 Internal Protected 亦可)。
- Family:開放給 Sub-Type 的程式。在 C# 中稱為 Protected。
- Assembly:開放給同一個 Assembly 的程式。在 C# 中稱為 Internal。
- Family-and-Assembly:開放給同一個 Assembly 的 Sub-Type 程式。C# 不支援這種 accessibility。
- Private:僅能在同一個 Enclosing Type (包含此 Nested Type 的外部 Type) 程式內使用。這也是 C# 的預定。
關於 CTS,如果讀者需要更進一步的資料,可以參考下面三本書:
- The Common Language Infrastructure Annotated Standard (by James S. Miller ) Addison Wesley
- Inside Microsoft .NET IL Assembler (by Serge Lidin) Microsoft Press
- Compiling for the .NET Common Language Runtime (by John Gough) Prentice Hall
意見與支援
您有任何問題、意見或建議嗎?您可以透過下列電子郵件與作者連絡:
xy.cai@msa.hinet.net
更多資訊
想知道大內高手專欄的其他文章嗎?請至此專欄所有列表