将类型生成到模块中
本节讨论如何将源代码文件转换成可以部署的文件。先看下面这个简单的示例:
-
创建文本文件Program.txt,打开记事本键入如下代码
public class Program { static void Main() { System.Console.WriteLine("Hello"); System.Console.ReadLine(); } }
- 将文件的后缀名修改为.cs
-
在命令行执行以下命令:
C:WindowsMicrosoft.NETFramework64v4.0.30319csc.exe /out:Program.exe /t:exe /r:MSCorLib.dll Program.cs
执行结果如下:
命令说明:
MSCorLib.dll是特殊文件,它包含所有核心类型。由于这些类型使用频繁,以至于C#编译器会自动引用MSCorLib.dll程序集,也就是说,上述命令中的/r:MSCorLib.dll可以省略。又因为/out:Program.exe和/t:exe开关是C#编译器的默认设定,所以上述命名可以继续简化为C:WindowsMicrosoft.NETFramework64v4.0.30319csc.exe Program.cs
-
步骤3中的命令执行完毕后,会在相应路径下生成可执行文件Program.exe,双击该可执行文件,结果如下:
响应文件
响应文件是包含一组编译器命令行开关的文本文件,执行csc.exe时,编译器打开响应文件,使用其中包含的所有开关。.NET Framework安装时会在%SystemRoot%Microsoft.NETFramework(64)vX.X.X目录中安装默认全局CSC.rsp文件(X.X.X是安装的.NET Framework版本号),全局CSC.rsp文件引用了列出的所有程序集,所以不必使用C#编译器的/reference开关显示引用这些程序集。
元数据概述
元数据是由几个表构成的二进制数据块。分别是定义表、引用表和清单表。编译器在编译源代码时,代码定义的任何项都会在定义表中创建一个记录。常用的元数据定义表如下:
元数据定义表名称 | 说明 |
---|---|
ModuleDef | 包含对模块进行标识的一个记录项。该记录项包含模块文件名和扩展名,以及模块版本ID |
TypeDef | 记录模块中定义的类型在。包含类型的名称、基类型、一些标志和索引,这些索引指向MethodDef、FieldDef、PropertyDef、EventDef中的成员 |
MethodDef | 记录模块中定义的方法 |
FieldDef | 记录模块中定义的字段 |
ParamDef | 记录模块中定义的参数 |
PropertyDef | 记录模块中定义的属性 |
EventDef | 记录模块中定义的事件 |
此外,编译器还会检测源代码引用的类型、字段、方法、属性和事件,并创建相应的引用表,它们记录了引用的内容。
引用表名称 | 说明 |
---|---|
AssemblyRef | 记录模块引用的每个程序集 |
ModuleRef | 记录该模块所引用的类型的每个PE模块 |
TypeRef | 记录模块引用的每个类型 |
MemberRef | 记录模块引用的每个成员 |
要查看元数据的相关内容,可以使用微软提供的IL反汇编工具ILDasm.exe检查托管PE文件中的元数据,所在目录:
C:Program Files (x86)Microsoft SDKsWindowsv10.0AinNETFX 4.6.1 Tools
使用ILDasm.exe打开上一节中的Program.exe程序集,选择视图|元信息|显示!,随后会显示如下信息:
从元信息文件中我们大致可以看出Program.exe中包含名为Program的TypeDef,这是一个公共类,从System.Object类派生,Program类型还定义了两个方法:Main和.ctor(构造器)。
将模块合并成程序集
程序集是一个或多个类型定义文件及资源文件的集合。程序集中包含一个清单文件,主要包含作为程序集组成部分的文件名称、程序集版本、语言文化等描述性内容。CLR操作程序集,首先加载包含清单元数据表的文件,在根据清单来获取程序集中的其他文件的名称。
为什么要引入程序集的概念?
因为使用程序集,可重用类型的逻辑表示与物理表示可以分开。
使用多文件程序集的理由:
- 不同的类型用不同的文件,使文件能以“增量”方式下载(将常用类型和不常用类型分开)。
- 可在程序集中添加资源或数据文件。
- 程序集包含的各个类型可以用不同的编程语言来实现。
清单元数据表简介:
清单元数据表名称 | 说明 |
---|---|
AssemblyRef | 主要包含程序集名称、版本、语言文化等内容 |
FileDef | 记录程序集中每个PE文件和资源文件,若程序集只包含模块本身,不包含其他模块和资源文件,FileDef表将无数据 |
ManifestResourceDef | 记录程序集中包含的每个资源的名称、标志以及FileDef表中的一个索引 |
ExportedTpyesDef | 记录从程序集的所有PE模块中导出的每个public类型 |
下面,我们将通过一个示例来说明如何生成多文件程序集。
-
首先准备两个源代码文件
-
CommonTypes.cs
public class CommonTypes { public static void Main() { System.Console.WriteLine("Hello,This is CommonTypes"); System.Console.ReadLine(); } }
-
UncommonTypes.cs
public class UncommonTypes { public static void Main() { System.Console.WriteLine("Hello,This is UncommonTypes"); System.Console.ReadLine(); } }
-
-
将UncommonTypes编译到一个单独的模块中,生成文件UncommonTypes.netmodule
C:WindowsMicrosoft.NETFramework64v4.0.30319csc.exe /t:module UncommonTypes.cs
-
将CommonTypes编译到另一个模块,该模块将成为程序集清单的宿主,因为这些类型会经常使用。生成文件MultiFileLibrary.dll
C:WindowsMicrosoft.NETFramework64v4.0.30319csc.exe /out:MultiFileLibrary.dll /t:library /addmodule:UncommonTypes.netmodule CommonTypes.cs
-
使用ILDasm.exe工具检查元数据清单表,如下图可知程序集文件确实包含了对UncommonTypes.netmodule文件类型的引用。
当有项目引用MultiFileLibrary.dll时,编译器会在搜索外部类型时加载MultiFileLibrary.dll以及FileDef表中列出的所有文件,也就是说,如果删除UncommonTypes.netmodule文件,编译时会报错
程序集版本资源信息
CSC.exe生成PE文件程序集时,会在PE文件中嵌入标准的Win32版本资源。可查看文件属性来检查该资源。
版本号格式说明
版本号格式 | major(主版本号) | minor(次版本号) | build(内部版本号) | revision(修订号) |
---|---|---|---|---|
示例 | 2 | 5 | 719 | 2 |