什么是元数据?
元数据是描述数据(类型信息)的数据,是有一组数据表构成的一个二进制数据块。元数据被CLR编译器编译后保存在Windows可移植执行体(PE)文件中,即和它描述的IL嵌入在EXE/DLL文件中。
怎么查看托管代码的元数据?
使用ildasm.exe打开EXE/DLL文件,并按Ctrl+M组合键或执行视图/元信息/显示将发现有关类型元数据。
元数据都有那些数据表构成?
元数据主要的类型表:
- 定义表 描述当前程序集中定义的类型和成员信息 主要包括:TypeDef、MehodDef、FieldDef、ModuleDef、PropertyDef等。
- 引用表 描述任何一个被内部类型引用的外部的类型和成员信息,引用元素可以是同一程序集的其他模块,也可以是不同程序集的模块 主要包括:AssemblyRef、TypeRef、ModuleRef、MethodsRef等。
- 清单表 描述了构成程序集的所有文件
- 指针表 使用指针表引用未知代码,主要包括:MethodPtr、FieldPtr、ParamPtr等。
- 堆 以stream的形式保存的信息堆,主要包括:#String、#Blob、#US、#GUIDe等。
在.NET中每个模块包含了44个CLR元数据表,如下:
表记录 | 元数据表 | 说明 |
0(0) | ModuleDef | 描述当前模块 |
1(0x1) | TypeRef | 描述引用Type,为每个引用到类型保存一条记录 |
2(0x2) | TypeDef | 描述Type定义,每个Type将在TypeDef表中保存一条记录 |
3(0x3) | FieldPtr | 描述字段指针,定义类的字段时的中间查找表 |
4(0x4) | FieldDef | 描述字段定义 |
5(0x5) | MethodPtr | 描述方法指针,定义类的方法时的中间查找表 |
6(0x6) | MethodDef | 描述方法定义 |
7(0x7) | ParamPtr | 描述参数指针,定义类的参数时的中间查找表 |
8(0x8) | ParamDef | 描述方法的参数定义 |
9(0x9) | InterfaceImpl | 描述有哪些类型实现了哪些接口 |
10(0xa) | MemberRef | 描述引用成员的情况,引用成员可以是方法、字段还有属性。 |
11(0xb) | Constant | 描述了参数、字段和属性的常数值 |
12(0xc) | CustomAttribute | 描述了特性的定义 |
13(0xd) | FieldMarshal | 描述了与非托管代码交互时,参数和字段的传递方式。 |
14(0xe) | DeclSecurity | 描述了对于类、方法和程序集的安全性 |
15(0xf) | ClassLayout | 描述类加载时的布局信息 |
16(0x10) | FieldLayout | 描述单个字段的偏移或序号 |
17(0x11) | StandAloneSig | 描述未被任何其他表引用的签名 |
18(0x12) | EventMap | 描述类的事件列表 |
19(0x13) | EventPtr | 描述了事件指针,定义事件时的中间查找表 |
20(0x14) | Event | 描述事件 |
21(0x15) | PropertyMap | 描述类的属性列表 |
22(0x16) | PropertyPtr | 描述了属性指针,定义类的属性时的中间查找表 |
23(0x17) | Property | 描述属性 |
24(0x18) | MethodSemantics | 描述事件、属性与方法的关联 |
25(0x19) | MethodImpl | 描述方法的实现 |
26(0x1a) | ModuleRef | 描述外部模块的引用 |
27(0x1b) | TypeSpec | 描述了对TypeDef或者TypeRef的说明 |
28(0x1c) | ImplMap | 描述了程序集使用的所有非托管代码的方法 |
29(0x1d) | FieldRVA | 字段表的扩展,RVA给出了一个字段的原始值位置 |
30(0x1e) | ENCLog | 描述在Edit-And-Continue模式中哪些元数据被修改过 |
31(0x1f) | ENCMap | 描述在Edit-And-Continue模式中的映射 |
32(0x20) | Assembly | 描述程序集定义 |
33(0x21) | AssemblyProcessor | 未使用 |
34(0x22) | AssemblyOS | 未使用 |
35(0x23) | AssemblyRef | 描述引用的程序集 |
36(0x24) | AssemblyRefProcessor | 未使用 |
37(0x25) | AssemblyRefOS | 未使用 |
38(0x26) | File | 描述外部文件 |
39(0x27) | ExportedType | 描述在同一程序集但不同模块,有哪些类型 |
40(0x28) | ManifestResource | 描述资源信息 |
41(0x29) | NestedClass | 描述嵌套类型定义 |
42(0x2a) | GenericParam | 描述了泛型类型定义或者泛型方法定义所使用的泛型参数 |
43(0x2b) | MethodSpec | 描述泛型方法的实例化 |
44(0x2c) | GenericParamConstraint | 描述了每个泛型参数的约束 |
然后是6个命名堆:
堆 | 说明 |
#String | 一个AscII string数组,被元数据表所引用,来表示方法名、字段名、类名、变量名以及资源相关字符串,但不包含string literals。 |
#Blob | 包含元数据引用的二进制对象,但不包含用户定义对象 |
#User Strings | 记录字符串字面量 一个unicode string数组,包含了定义在代码中的字符串(string literals),这些字符串可以直接由ldstr指令加载获取,还记得吗?我们在《第二十二回:字符串驻留(上)---带着问题思考》中对字符串创建过程的论述吗? |
#GUID | 保存了128byte的GUID值,由元数据表引用 |
#~ | 一个特殊堆,包含了所有的元数据表,会引用其他的堆。 |
#- | 一个未压缩的#~堆。除了#-堆,其他堆都是压缩的。 |
Note:对于#String和#User Strings,一个简单的区别就是:string hello = "Hello, World";变量hello名,将保存在#String,而代码中字符串信息“Hello, World”则被保存在#US中。
元数据的用途
- 编译时,元数据消除了对本地C/C++头和库文件的需求,因为在负责在实现类型/成员的IL代码文件中,已包含和引用的类型/成员有关的全部信息。编译器可以直接从托管模块读取元数据。
- VS使用元数据帮助您写代码。它的“智能感知”(IntelliSense)技术可以解析元数据,指出一个类型提供了哪些方法、属性、事件和字段。
- 许多.NET技术,如对象序列化、.NET远程处理、XML WEB服务、以及WCF都需要元数据在运行时发现类型格式。
- 元数据允许垃圾回收器跟踪对象的生存期。垃圾回收器能判断任何对象的类型,并从元数据知道那个对象中的哪些字段引用了其它对象。
参考文章: [你必须知道的.NET]第二十五回:认识元数据和IL(中)
作者:代码哥
出处:http://daimage.cnblogs.com/
说明:本博原创文章版权归博客园和本人共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出作者名称和原文连接,否则保留追究法律责任的权利。