元数据
什么是元数据?
元数据是描述数据(类型信息)的数据,是有一组数据表构成的一个二进制数据块。元数据被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/
说明:本博原创文章版权归博客园和本人共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出作者名称和原文连接,否则保留追究法律责任的权利。
作者:代码哥
出处:http://daimage.cnblogs.com/
说明:本博原创文章版权归博客园和本人共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出作者名称和原文连接,否则保留追究法律责任的权利。

浙公网安备 33010602011771号