元数据
元数据,就是描述数据的数据。在CLR中,元数据就是对一个模块定义或引用的所有东西的描述系统。因为CLR编程系统是面向对象的,那么元数据描述的东西就是类和他们的成员及其伴随而来的特性(attributes)属性(properties)和联系。(relationships)。
从实际的角度看,元数据的作用和com中的类型库(type libery)很象。不过区别还是很大的,元数据描述模块或装配的结构的每一个细节,而类型库只提供模块暴露出来的com接口的信息,元数据提供多得多的信息。另外元数据是存在于托管模块里面的,它允许每个托管模块自带一个关于它自己的逻辑结构的完整的正式的标准的描述。
元数据又是一个标准的关系数据库。就是说元数据被组织成许多互相引用的矩形表。一个表的每一个位置包含一个数据或者对另一个表的一行的引用。元数据中的数据没有冗余。每一种数据只存在于元数据库的一个表中,如果另一个表需要使用相同的数据,它就引用那个存放这个数据的表。
比如刚开始(上一篇文章)的时候给的那个例子表明的,一个类的定义会包括一些特定的二进制属性,而这个类的成员函数的行为和特征会受到拥有这个函数的类的属性的影响,描述这个函数的时候就需要复制这个类的属性。可是数据的复制不仅会增大数据库的体积,还会使得数据的不同拷贝难以同步。
实际上,一个成员方法的描述(descriptor)包含一个对包含这个方法的类的引用。当然,这样的引用需要更多的处理器时间,但是对于大型的分布式系统来说(.net框架设计的目标就是这类的系统),处理器的速度不是问题,而传输带宽和数据正确才是最重要的。
那么,如果你需要找出一个特定类的所有方法你会怎么做呢?是浏览整个方法描述表去找出所有指向这个类的描述的方法?这样就太傻了。实际上,类的描述(一个记录)包含一个引用,指向这个类的第一个方法的描述(也是一个记录),属于这个类的方法表的结尾就是下一个类的方法表的开始或者是整个方法表的结尾。
显然,这种技术需要方法表中的记录必须按照他们所属的类进行排序。这个技术同样应用于其他的表-表关系(class-field, method-parameter,and so on) 。只要符合这个要要,元数据就可以说是最佳的或者说压缩了的。ILasm编译器总是会产生最佳的元数据:)。如下图:
然而,在某些坏的情况下还是有可能会使元数据表产生交叉存取,比如class A 的记录先产生,然后是classB ,class B 的方法记录,class A 的方法记录。或者是class A ,class B ,A 的部分方法,B的方法,然后是A剩下的方法。
在这样情况下,就需要另外添加一个中间表。类的描述记录会引用这个中间表(指针表),而不是直接引用方法记录。这个中间表会按顺序引用方法记录。使用了中间表的元数据就不是最佳的或说没被压紧,如下图: