2009年9月7日

.NET CTS (Common Type System)

.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 中有下列三種因應方式:

  1. 改變語言本身以符合 CTS 的規定。例如 Visual Basic 6 就為此做了大幅度的擴充與改變,加上繼承的特性,這使得新版的 Visual Basic .NET 差不多可以算是一個全新的語言了,猶如當年 C 衍生出 C++ 一樣。
  2. 擴 充語言本身以接近 CTS 的規定,但仍保留不相容於 CTS 的語法,如此一來,程式中符合 CTS 規定者以 CTS 的方式編譯,不符合 CTS 規定者則以傳統的方式編譯成 native code。例如 C++ with Managed Extension (簡稱 MC++) 就是如此。
  3. 語 言本身儘量維持不變,一切都是透過超強的編譯器設計來達成和 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 姓其召語言的裧咳
圖 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︹守綿剛的邕樹樥配置方式
圖 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 的一函重要型吶
圖 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
圖 4:Value Type、Reference Type、以及 Interface 的 Accessibility

如果是 Nested Type (定義在另一個 Type 內部的 Type) 的話,則有比較多種 Accessibility。如下圖五所示:

對 5︹nested 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,如果讀者需要更進一步的資料,可以參考下面三本書:

  1. The Common Language Infrastructure Annotated Standard (by James S. Miller ) Addison Wesley
  2. Inside Microsoft .NET IL Assembler (by Serge Lidin) Microsoft Press
  3. Compiling for the .NET Common Language Runtime (by John Gough) Prentice Hall

沒有留言: