.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)。
圖一是整個概念的示意圖。
圖 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 (拆箱)。
圖 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
後面會一一解說這些型別。圖三是這些型別的繼承架構。
圖 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。
圖 4:Value Type、Reference Type、以及 Interface 的 Accessibility
如果是 Nested Type (定義在另一個 Type 內部的 Type) 的話,則有比較多種 Accessibility。如下圖五所示:
圖 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
沒有留言:
張貼留言