您的位置:首页 > 编程语言 > C#

CLR via C# 读书笔记2-4

2013-05-22 11:15 204 查看
从模块到程序集

前文的 Program.exe 不仅是一个包含元数据的 PE 文件,也是一个程序集。所谓的程序集是一个或多个包含类型定义或资源的文件的集合。程序集中的一个文件被用来存放 manifest,这个 manifest 是另一个元数据表的集合,其中包含了程序集中所有文件的名称,以及程序集的版本,culture,发布者,公有类型等信息。CLR 处理程序及时,总是首先打开这个程序集中的 manifest 文件,以获取程序集的信息。

以下是程序集的特点:

■ 程序集定义了可重用的类型

■ 程序集有一个版本号

■ 程序集可以关联安全信息

程序集中的文件(除了上文提到的 manifest 文件)都没有以上的特性。通常情况下程序集仅包含一个文件(如前文的 Program.exe),但它实际上可以由多个文件(多个PE 文件以及多个资源文件)组成。

程序集作为一个可重用的单位,你可以把常用和不常用的功能类型进行分隔。以下是使用程序集的场景:

■ 类型分布在多个文件,可以通过网络被增量下载,同样也有利于分开打包发售。

■ 为你的程序集添加资源或数据文件。比如,你有一个计算保险的类型,这个类型需要访问一些表数据。你可以通过使用类似Assembly Linker(AL.exe)的工具把这些表合并到程序集中而不是直接在源代码中包含这些表。这些表可以是任何你的程序能解析的格式: text 文件,Excel 文件,Word 文件等等。

■ 建立不同开发语言的程序。比如,你可以同时使用 C# 或 Visual Basic 或其他语言,并通过它们各自的编译器编译为独立的模块,然后使用工具把这些模块集成到一个程序集中。如果你愿意,你可以使用 ILDasm.exe 打开所有这些模块,并提取 IL 源代码文件,然后使用 ILAsm.exe 把这些源代码文件合成为一个文件。

注意:如果你有多个类型,并属于同一版本同一安全设定,那么为了获得更好的性能,把它们合在一个文件中是比较好的做法。因为载入一个文件/程序集会使 CLR 花费相当的时间去定位程序集,载入并初始化,所以载入的文件/程序集数量越少越好。

要生成一个程序集,你必须选择一个既有的 PE 文件或者新建一个空的 PE 文件来包含 manifest。表 2-3 显示了把托管模块转变为程序集的 manifest 的 元数据表:

表 2-3 Manifest 元数据表

Manifest 元数据表名说明
AssemblyDef包含该模块是否是一个程序集的条目,包含了程序集的名称,版本,culture, 标志, 哈希值以及发布者公钥
FileDef程序集中每个 PE 或资源文件的条目(除了包含manifest的那个文件),它包含了文件名,哈希值,标志。如果程序集仅包含自身一个文件,那么 FileDef 表为空。
ManifestResourceDef程序集中每个资源的条目),它包含了资源名,标志(public,private),以及对应到FileDef 表中数据的索引。
ExportedTypesDef程序集中每个公有类型的条目,包含了类型名称,对应到FileDef 表的索引(指名该类型来自哪个文件),对应到 TypeDef 表的索引。
这些 manifest 使得程序集是“自我描述”的,同时明确指定哪些文件是该程序集的组成部分。(但是反过来,这些文件本身并不包含 manifest 来表明它们是程序集的一部分)。

当你在编译命令行上使用: /t[arget]:exe, /t[arget]:winexe, /t[arget]: appcontainerexe, /t[arget]:library, 或 /t[arget]:winmdobj 时,编译器会生成为程序集(包含manifest 元数据表的PE 文件)。除了这些编译开关, C# 编译器还支持 /t[arget]:module 开关,该开关生成一个不包含manifest 元数据表的PE 文件(一个DLL PE文件)。这种文件只有被组入一个程序集后才能被 CLR
解析访问,默认的后缀名为 .netmodule。

注意:Microsoft Visual Studio IDE 并没有内置支持以上的功能,所以你只能通过命令行来完成。

有许多方式来把一个模块组入到一个程序集中,如果你使用 C# 编译器你可以使用 /addmodule 开关。以下是个示例,首先假设你有2个文件:

■ RUT.cs,包含一些不常用的类型

■ FUT.cs,包含了一些常用的类型

先编译 RUT.cs (csc /t:module RUT.cs),这会生成一个名为 RUT.netmodule 的文件。

接着编译 FUT.cs,同时组入RUT.netmodule (csc /out:MultiFileLibrary.dll /t:library /addmodule:RUT.netmodule FUT.cs) 。这最终生成一个名为 MultiFileLibrary.dll 的程序集文件,文件内部结构参见图2-1:



使用 ILDasm.exe 来验证一下:

File #1 (26000001)
-------------------------------------------------------
Token: 0x26000001
Name : RUT.netmodule
HashValue Blob : e6 e6 df 62 2c a1 2c 59 97 65 0f 21 44 10 15 96 f2 7e db c2
Flags : [ContainsMetaData] (00000000)
ExportedType #1 (27000001)
-------------------------------------------------------
Token: 0x27000001
Name: ARarelyUsedType
Implementation token: 0x26000001
TypeDef token: 0x02000002
Flags : [Public] [AutoLayout] [Class] [Sealed] [AnsiClass]
[BeforeFieldInit](00100101)

当其他客户端代码参照 MultiFileLibrary.dll 程序集运行的时候,CLR 首先在程序集中包含 manifest 的那个文件中检索需要的类型,如果能够找到 CLR 会内部登记一下然后使用,如果未检索到,但是通过 manifest 获知在程序集的另一个组成文件中, CLR 会载入这个文件然后内部登记后并使用。如果该程序集中找不到,那么CLR 会查找其他的程序集。这就意味着运行一个程序,并不需要参照程序集中的文件都参与。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: