返回目錄
類的擴充
在ILAsm中,和在Visual Basic和C#中,一個類的所有的成員、特性和內嵌類都在這個類的詞法範圍內聲明。然而,ILAsm允許你重新開啟一個已經關閉的類範圍並定義額外的項:
.class public X extends Y implements IX,IY {
}
// Later in the source, possibly in another source file
.class X {
// More items defined
}
這種對類範圍的重新開啟被稱為類的擴充。一個類可以遍及原始碼被擴充任意多次,而且擴充模組可以位於不同的源檔案中。下面的簡單安全性規則管理著類的擴充:
類必須完全定義在模組中——換句話說,你不可以擴充一個定義在別的什麼地方的類。(難道這樣不好嗎?再見吧,安全——完美的狀態!)
Class標記,extends子句,以及implements子句必須完全在詞法上定義在類的首次開啟的範圍內,因為這些特性在擴充部分中是被忽略的。
擴充部分不可以包括重複項的聲明。如果你聲明int32欄位X在一個片斷中,然後又在另一個片斷中聲明它,ILAsm編譯器將不會感激你可能成心要有兩個相同的欄位,並將讀取它作為在一個相同的類中定義兩個同樣欄位的一個嘗試,而這是不允許的。
以1.0版本和1.1版本寫一個ILAsm程式,一個好的策略是使用前向類聲明(forwarder class declaration),在第一章解釋過。這個策略允許你聲明當前模組的所有類,包括內嵌類,不帶任何成員和特性,並且在擴充部分中定義成員和特性。這種方式,IL編譯器在任意類型被引用之前就充分瞭解模組的型別宣告結構。到本地聲明的類型被引用的時候,它們已經全都被定義了並且有相應的TypeDef中繼資料記錄。
可是,在ILAsm的2.0版本中不再需要前向類聲明了。在2.0版本中,I編譯器隱式地聲明一個類無論何時這個類被提到,作為一個聲明或者作為一個引用。當然,這個隱式聲明在一個引用上的類僅僅是一個啞元——預留位置。當這個類的聲明{.class … { … }}在原始碼中被遇到的時候,傳遞一個啞元到“真實”的類聲明。如果所有的編輯被分析了,並且這裡仍然留有“啞元”,編輯就會失敗。
擴充部分並沒有被顯示地數字化,而且這個類是根據擴充部分在原始碼中的順序來擴充的。這意味著如果你交換擴充部分,這將依次影響類的布局,那麼類的項聲明順序將會改變。
可是,如果你想保留類聲明的順序或者你考慮以某個特定的順序發布這個類的聲明,你可以使用指令.typelist:
.typelist { FirstClass SecondClass ThirdClass ...}
.Typelist指令最好放在第一個源檔案的頂部,如果存在的話,甚至在清單聲明之前而在.mscorlib指令之後。這樣放置的原因是明顯的:IL編譯器需要立刻知道你是否正在編譯Mscorlib.dll或者其它別的什麼,而且清單聲明可能有自訂特性,或者其它類的引用,這可能混合類聲明的原有順序。
清單聲明,在第6章描述過,加上前向類聲明(1.0和1.1版本)或.Typelist指令(2.0版本),看上去像一個程式頭,因此我不會責備你,如果你把它們放在一個單獨的源檔案中。只是不要忘記,當你編譯器集時,這個檔案必須優先位於源檔案之中。
中繼資料驗證規則小結
回想類型相關的中繼資料表(除去那些被涉及到的泛型,這將在第11章討論)包括了TypeDef、TypeRef、InterfaceImpl、NestedClass和ClassLayout。這些表的記錄包括了下面的項:
TypeDef表包括了Flags、Name、Namespace、Extends、FieldList和MethodList項。
TypeRef表包括了ResolutionScope、Name和Namespace項。
InterfaceImpl表包括了Class和Interface項。
NestedClass表包括了NestedClass和EnclosingClass項。
ClassLayout表包括了PackingSize、ClassSize和Parent項。
TypeDef表驗證規則
Flags項只可以具有那些定義在CorHdr.h中的CorTypeAttr枚舉的位設定。除了tdFowarder這個為匯出類型(驗證標記:0x00173DBF)所保留的標記。
[runtime] Flags項不可以同時具有sequential和explicit位設定。
[runtime] Flags項不可以同時具有unicode和autochar位設定。
如果在Flags項中設定了rtspecialname標記,Name欄位就必須被設定為_Deteled*,反之亦然。
[runtime] 如果在Flags項中設定了0x00040000位,DeclSecurity記錄或名為SuppressUnmanagedCodeSecurityAttribute的自訂特性必須與TypeDef聯合,反之亦然。
[runtime] 如果在Flags項中設定了interface標記,就必須設定abstract。
[runtime] 如果在Flags項中設定了interface標記,就必須設定sealed。
[runtime] 如果在Flags項中設定了interface標記,TypeDef必須沒有執行個體欄位。
[runtime] 如果在Flags項中設定了interface標記,TypeDef的所有執行個體方法必須是抽象的。
[runtime] 如果TypeDef的可見度標記被設定為nested private,nested family,nested assembly,nested famorassem或nested famandassem,TypeDef必須在NestedClass中繼資料表一筆記錄的NestedClass項中被引用,反之亦然。
Name欄位必須引用一個#Strings流中的非Null 字元串。
由Name和Namespace引用的字串的聯合長度不可以超過1023位元組。
TypeDef表不可以包括具有相同全名(命名空間加上名稱)的重複記錄,除非TypeDef是內嵌的或者被刪除的。
[runtime]對於帶有interface標記設定的TypeDef 和Mscorlib程式集的System.Object,Extends項必須是零。(這句話貌似原文筆誤)
[runtime] 所有其它TypeDef的Extends項必須儲存一個指向TypeDef、TypeRef或TypeSpec表的有效引用,並且這個引用必須指向一個非密閉的類。
[runtime] Extends項不可以指向類型本身或者該類型的子孫(繼承迴圈)。
[runtime] FieldList項必須是零或者儲存對Field表的一個有效引用。
[runtime] MethodList項必須是零或者儲存對Method表的一個有效引用。
Enumeration-Specific驗證規則
如果TypeDef是一個枚舉——就是說,如果Extends項儲存了對[mscorlib]System.Enum的引用——適用於下面額外的規則:
[runtime] interface、abstract、sequential、和explicit標記不可以在Flag項中被設定。
Sealed標記必須在Flag項中被設定。
TypeDef不可以有方法、事件或屬性。
TypeDef不可以實現介面——就是說,它不可以在InterfaceImpl表的任何記錄中的Class項被引用。
[runtime] TypeDef必須至少有一個整型或布爾或字元類型的執行個體欄位。
[runtime] TypeDef的所有靜態欄位必須是文本化的。
TypeDef的靜態欄位的類型必須是當前的TypeDef本身。
TypeRef表驗證規則
[runtime] ResolutionScope項必須儲存0或者一個指向AssemblyRef、ModuleRef、Module或TypeRef表的有效引用。在最後一種情形中,TypeRef指向一個內嵌在另一個類型中的類型(一個內嵌的TypeRef)。
如果ResolutionScope項為0,這個程式集的主模組的ExportedType表必須包括一筆記錄,它的TypeName和TypeNamespace項相應地與TypeRef記錄Name和Namespace項匹配。
[runtime]Name項必須引用#Strings流中一個非空的字串。
InterfaceImpt表驗證規則
將Class項設定為0意味著一筆已刪除的InterfaceImpt記錄。然而,如果Class項是非0的,那麼就適用下面的規則:
[runtime] Class項必須保持對TypeDef表的一個有效引用。
[runtime] Interface項必須保持對TypeDef或TypeRef表的一個有效引用。
如果Interface欄位引用了TypeDef表,相應的TypeDef記錄必須在Flags項設定interface標記。
這個表不可以包括帶有相同Class和Interface項的重複記錄
NestedClass表驗證規則
[runtime] NestedClass項必須保持對TypeDef表的一個有效引用。
[runtime] EnclosingClass項必須保持對TypeDef表的一個有效引用,一種不同於由NestedClass項保持的引用。
這個表不可以包括帶有相同EnclosingClass和NestedClass項的記錄,這些項引用了帶有匹配名稱的TypeDef記錄——換句話說,一個內嵌類在它的外包類中必須有一個唯一的名稱。
這個表不可以包括一組形成迴圈嵌套模式的記錄——例如,A內嵌在B中,B內嵌在C中,C內嵌在A中。
ClassLayout表驗證規則
將Parent項設定為0意味著一筆已刪除的ClassLayout記錄。然而,如果Parent項是非0的,那麼就適用下面的規則:
Parent項必須保持對TypeDef表的一個有效引用,而且被引用的TypeDef記錄必須將Flags位設定為explicit或sequential,並且不可以設定interface位。
[runtime] PackingSize項必須設定為0或者1到128範圍內2的指數冪。
這個表不可以包括帶有相同Parent項的重複記錄。
terminology 術語
inherently 本質上
all of a sudden 突然
in a general sense籠統地
engage in 使從事於, 參加
中繼資料中的entry翻譯成項比較好
Resolution Scope 解析範圍
lookup table 尋找表
alphabetic character 文字元號
economy 節省
marshal 封送
at will 隨意
granularity 粒度
under construction
bear in mind 記住
stem from the fact 來源於
namely 也就是
trivial 平凡的
forward slash ---> /
back slash ------> "
in this regard 在這點上
one that 一種