在正式开始介绍反射的概念之前,我们先了解一下元数据。
元数据概述
C#代码在经过编译之后会得到二进制格式的程序集,程序集一般是一个.dll或.exe后缀的文件。
程序集一般包含四个部分:
PE32(+)头
CLR头
元数据
IL
PE32(+)头是Windows要求的标准信息。
CLR头是托管模块所特有的,一般包含以下信息:
CLR的版本号,
一些标志
模块的入口方法
强名称数字签名(可选)
元数据是由几个表构成的二进制数据块。有三种表分别是:
定义表(definition table)
引用表(reference table)
清单表(manifest table)
元数据-定义表包含哪些信息?
ModuleDef:标识模块的一个记录项,一般一个C#文件就是一个模块。
具体包含文件信息,版本信息等。
TypeDef:在程序集中定义的每一个类型都有一个记录项。
具体包含类型的名称,基类,以及一些指向MethodDef,FieldDef,PropertyDef和EventDef区域的索引。
MethodDef:在程序集中定义的每一个方法都有一个记录项。
具体包含方法签名和指向方法的IL代码的索引,以及指向方法参数ParamDef的索引。
FieldDef:在程序集中定义的每一个字段都有一个记录项。
具体包含类型,名称以及编译时期确定的静态值等信息。
ParamDef:在程序集中定义的每一个方法参数都有一个记录项。
具体包含类型,名称以及编译时期确定的默认值等信息。
PropertyDef:在程序集中定义的每一个属性都有一个记录项。
具体包含类型,名称等信息。
EventDef:在程序集中定义的每一个事件都有一个记录项。
具体包含委托的类型,名称等信息。
在编译源代码时,代码定义的任何东西都导致在元数据-定义表中创建一个与之对应的记录项。
元数据-引用表包含哪些信息?
AssemblyRef:在模块中引用的每一个程序集都有一个记录项。
具体包含程序集名称,版本,公钥等。
ModuleRef:在模块中引用的类型所在的每一个PE模块都有一个记录项。
具体包含模块的文件名,扩展名。
TypeRef:在模块中引用的每一个类型都有一个记录项。
具体包含类型的名称和一个指向该类型的位置的引用。
MemberRef:模块引用的每一个成员(字段,方法,属性,事件)这个表中都有一个记录项。
具体包含成员的名称和签名,以及一个指向定义该成员的那个类型的TypeRef记录项。
对于上面列出来的定义表和引用表,并不是编译器生成的全部信息,编译器还生成了很多其他的定义表和引用表。我这边只是列出了常用的部分。
程序集是一个或多个类型定义文件及资源文件的集合。在程序集的所有文件中,有一个文件容纳了清单。主要包含作为程序集组成部分的那些文件的名称,还描述了程序集的版本,语言文化,发布者,公开导出的类型以及构成程序集的所有文件。
CLR只认识程序集。CLR总是首先加载包含清单元数据表的文件,再根据清单来获取程序集中的其他文件的名称。
元数据-清单表包含哪些信息?
AssemblyDef:程序集标识记录项
包含程序集名称,版本,语言文化等
FileDef:作为程序集一部分的每一个PE文件和资源文件都有一个记录项。
具体包含文件名,扩展名等。
ManifestResourceDef:作为程序集一部分的每个资源都有一个记录项。
具体包含名称,以及资源的位置的索引。
ExportedTypesDef:从程序集中所有PE模块中导出的每一个public类型都有一个记录项。
具体包含类型名称,FileDef表的索引,TypeDef表的索引。
由于有了清单,我们不用关系程序集的划分细节,同时也使程序集具有自我描述性。
本文回顾:
程序集四个组成部分
元数据是由三种表构成的数据块
元数据-定义表
元数据-引用表
元数据-清单表