《Expert .NET 2.0 IL Assembler》 第一章 简单示例 1.3 类的预先声明

返回目录

 

     这一节只是相关于ILASM的早期版本(1.0和1.1),但是我仍然认为这是有用的信息。考虑一下这些.NET Framework版本的基本大小,你将会不止一次的遇到与这些ILASM的老版本打交道的机会。

     如果你安装了.NET Framework的早期版本,你可以对这个代码示例执行一些试验。在任意文本编辑器中打开这个源文件Simple.il并作出如下修改:将对值类型CharArray8的声明移动到字段Format的前面:

{


//  End of namespace
.class   public  explicit CharArray8
     
extends  [mscorlib]System.ValueType { .size  8   }
.field   public  static valuetype CharArray8 Format at FormatData

     事情看起来是井然有序的。但是当你试图编译这个文件时,ILAsm编译器将会失败并报出Unresolved MemberRef ‘Format’的错误信息。

     现在再次修改源文件,这次将对值类型CharArray8的声明移动到命名空间Odd.Or的前面:

.class   public  explicit CharArray8
                    
extends  [mscorlib]System.ValueType { .size  8   }
.namespace  Odd.or {
     
.class   public   auto   ansi  Even  extends  [mscorlib]System.Object {
          
.field   public  static  int32  val
          
.method   public  staticvoid check( )  cil   managed  {
               
               
ldsflda  valuetype CharArray8 Format
               
          
//  End of method
     
//  End of class
//  End of namespace
.field   public  static valuetype CharArray8 Format at FormatData

     现在,当你保存这段源代码并试图重编译它的时候,这就都回复正常了。这里面发生了什么呢?

     在第一次源代码改变后,当Format字段被check方法中的ldsflda指令引用时,值类型CharArray8还没有被声明,它的TypeRef就会因此被忽略,字段引用的签名就会收到该TypeRef作为它的类型。

     接着,值类型CharArray8被声明,一个新的TypeDef被创建。然后,当Format字段确实被声明时,它的类型被当成一个局部声明的值类型,而且该字段的签名定义接收到TypeDef作为它的类型。但是,不存在以TypeRef作为类型的名为Format的字段,被声明在模块中的任何地方。从而,你得到了引用到定义的决定性失败。

     (有一段时间批判ILAsm编译器在编程级别上缺少匹配签名的能力,伴随着类型分析和通过完全名称和决定范围来匹配TypeRef到TypeDef。然而,请耐心一些。)

     第二次改变源代码后,值类型CharArray8首次被声明——以便于所有的引用指向它,而不管这些引用在哪里出现,我们称这个类型为TypeDef。这是一个相当明显的解决方案。

     这个解决方案也会变得不明显——在你考虑两个类的时候,它们的成员互相使用对方的类型作为成员。那么先声明哪一个呢?准确地说,全部。

     在“类定义”章节我提到了类的修正技术,基于ILAsm,允许你重新打开类的范围从而声明更多类的特性和成员。解决这个声明/引用问题的基本方案是,首先为所有的类指定一个空范围类的定义。据此,你可以完全指定所有的类,包括它们的特性和成员,作为修正。“首轮”类的声明应该携带所有类标记、extends子句和implements子句,还应包括所有内嵌类(还有空范围)。你应该将所有这些成员声明维持到后期。

     这种类的预先声明的技术防止了声明/引用错误,而副作用是降低了元数据的大小,因为为了局部化定义的类而忽略多余的TypeRef是不必要的。

    (上述对ILAsm的评论,相应的解答是,编译器确实以最可能快的速度进行签名,而不需要更多高级并缓慢的方法,只要你使用到类的预先声明。)

     类的预先声明的需求,在ILAsm 2.0版本编译器中被消除了。

 

你可能感兴趣的:(.net)