元数据标记

元数据是指有关抽象的声明性信息,而抽象包括运行时类型(类、值类型和接口)、全局函数和全局变量。 元数据存储在表中 - 一个表对应于抽象的一个类别,表中的一行对应于抽象的一个声明。 标记(一个类型为 mdToken 的对象)用于查找包含抽象元数据的记录。 元数据引擎使用标记在给定元数据范围内的特定元数据表中编制索引。

元数据标记结构

元数据标记是一个长为 4 字节的值。 最有意义的字节 (MSB) 指定了标记类型,因此用于标识抽象及其关联的元数据表。 例如,如果 MSB 中的值为 1,则说明该标记是一个表示类型引用的 mdTypeRef 标记,并且其元数据存储在 TypeRef 元数据表中;如果 MSB 中的值为 4,则它对应于 mdFieldDef 标记。 CorTokenType 枚举用于指定标记类型。

其他三个低位字节称为记录标识符 (RID),其中包含标记的 MSB 所引用的元数据表中的行索引。 例如,值为 0x02000007 的元数据标记引用当前范围内 TypeDef 表的第 7 行。 同理,标记 0x0400001A 则引用当前范围内 FieldDef 表的第 26(十进制)行。 元数据表中的第零行始终不会包含数据,因此 RID 为零的元数据标记称为“零标记”。 元数据 API 会定义许多这样的零标记,其中每个标记类型都有一个零标记,例如值为 0x01000000 的 mdTypeRefNil

备注

上文从概念上对 RID 进行了解释;在实际中,元数据的物理布局要复杂得多。另外,字符串标记 (mdString) 稍有不同,它的 3 个低位字节并非记录标识符,而是表示该字符串在元数据字符串池中的起始位置的偏移量。

使用元数据标记

元数据 API 中的每个 DefineXXX 方法都会返回一个标记,通过将该标记传递给 GetXXX 方法,可以获取其关联特性。

元数据标记是在范围内定义的。 例如,在某个给定的范围内,值为 N 的元数据标记完全标识一个包含有关某个类型定义的详细信息的记录。 而在另一个不同的范围内,即使元数据标记具有相同的值 N,也可能指定完全不同的记录。

元数据标记不是不可变的元数据对象标识符。 当两个范围合并时,来自导入范围的标记将重新映射到已发出范围内的标记。 在保存元数据范围时,各种格式优化均可导致重新映射标记。

标记类型

下表列出了元数据标记类型、每个标记类型表示的抽象以及包含该抽象元数据的元数据表的名称。 所有标记类型都是基本标记类型 mdToken 的变体。 mdToken 定义在 CorHdr.h 头文件中,如下所示:

typedef ULONG32 mdToken;

标记类型

元数据表

抽象

mdModule

模块

模块:编译单元、可执行文件或某个其他开发单元、部署单元或运行时单元。 可以从总体上声明模块的特性(尽管不需要这么做),包括名称、GUID、自定义特性等等。

mdModuleRef

ModuleRef

模块引用:对模块的编译时引用,用于记录类型和成员的导入源。

mdTypeDef

TypeDef

类型声明:运行时引用类型(类或接口)或值类型的声明。

mdTypeRef

TypeRef

类型引用:对运行时引用类型或值类型的引用。 从某种意义上来讲,模块中的类型引用集合就是编译时导入依赖关系的集合。

mdMethodDef

MethodDef

方法定义:将方法定义为类或接口的成员,或将方法定义为全局模块级方法。

mdParamDef

ParamDef

参数声明:定义用于存储参数的附加元数据的可选数据结构。 不必为方法中的每个参数都发出一种数据结构。 但是,当参数具有要保留的附加元数据(例如封送处理和类型映射信息)时,可以创建可选参数数据结构。

mdFieldDef

FieldDef

字段声明:将变量声明为类或接口的数据成员或声明全局模块级变量。

mdProperty

属性

属性声明:将属性声明为类或接口的成员。

mdEvent

Event

事件声明:将命名事件声明为类或接口的成员。

mdMemberRef

MemberRef

成员引用:对方法或字段的引用。 对于由当前模块中的任何实现所进行的每一次方法调用或字段访问,都将在元数据中生成一个成员引用,并且会在 Microsoft 中间语言 (MSIL) 流中保留一个标记。 属性或事件引用没有运行时支持。

mdIfaceImpl

IfaceImpl

接口实现:特定类对特定接口的实现。 此元数据抽象允许存储既不特定于该类也不特定于该接口的信息交集。

mdMethodImpl

MethodImpl

方法实现:特定类对使用接口继承进行继承的方法的实现。 此元数据抽象允许保留特定于该实现(而不是特定于协定)的信息。 实现类无法修改方法声明信息。

mdCustomAttribute

CustomAttribute

自定义特性:一个与可使用 mdToken 引用的任何元数据对象关联的任意数据结构。 (例外情况是自定义特性本身不能具有自定义特性)。

mdPermission

权限

权限集:与 mdTypeDefmdMethodDefmdAssembly 关联的声明性安全权限集。 有关详细信息,请参阅添加声明式安全支持

mdTypeSpec

TypeSpec

类型构造函数:一个获取类型(例如装箱的值类型)标记的方法,该类型可用作任何带有类型的 MSIL 指令的输入。

mdSignature

签名

独立签名:可迁移可执行 (PE) 文件中的局部变量签名或传递给 MSIL 指令的方法签名。

mdString

String

用户字符串:传递给 MSIL 指令的字符串。

备注

上面的列表未包括两个单独的标记类型,在一般情况下,其中一个标记类型用于字段引用,另一个用于方法引用。字段引用和方法引用共享同一个表,而且可以使用标记类型 mdMemberRef 加以引用。

扩展性和抽象

运行时元数据是可扩展的,这在以下方案中具有重要意义:

  • 表示由公共语言规范 (CLS) 定义的约束或较高级别的抽象。 CLS 是语言和工具为按统一方式提供支持以更好地实现语言集成而达成的约定规范。 CLS 不仅可以约束常规类型系统模型的各个部分,还可以在常规类型系统之上引入较高级别的抽象层。 对于工具使用的这些类型的开发时抽象,即使运行时未显式识别或支持它们,元数据也必须能够捕获它们。

  • 表示不属于常规类型系统且不是 CLS 抽象的语言特定的抽象。 通过此方案,像 Visual C 这样的语言便不需要单独的头文件或 IDL 文件即可使用由已编译模块导出的类型、方法和数据成员。

  • 对在特定于语言的重载中使用的成员内签名类型和类型修饰符进行编码。

元数据扩展性具有以下形式:

  • 每个元数据对象都可以支持自定义特性,并且元数据 API 提供了一种声明、枚举和检索自定义特性的方式。 自定义特性可以由类型引用(mdTypeDefmdTypeRef)进行标识。 自定义特性的结构使用类型上声明的数据成员进行自我描述,并且值编码可通过任何工具(包括运行时反射服务)进行浏览。

  • 除了对常规类型系统的扩展外,还可以向成员签名中发出自定义修饰符。 运行时将允许在方法重载、隐藏及绑定中使用这些修饰符,但不会强制使用任何特定于语言的语义。

请参见

其他资源

元数据概述