老白在上一篇理解C# 核心概念 – C# 模块(module)中讲解到了module,并且也提及到了我们编译处理的Program.exe就是一个module。很多同学可能就有疑问了,Program.exe是一个module吗?不是应该是一个Assembly吗?我们生成出来的dll不也是一个Assembly吗?因此,我们就来谈谈程序集(Assembly),这部分将分成上下两篇进行呈现。
- 理解C# 核心概念 – C# 程序集(Assembly)上
- 什么是Assembly
- 如何生成Assembly
- 理解C# 核心概念 – C# 程序集(Assembly)下
- 为什么需要Assembly
- 示例
- 生成多个module的Assembly
- 添加资源文件到Assembly
什么是Assembly
和上一篇的module一样,Assembly其实也是CLR的概念并非C#独有。任何一个编译出来的exe或者dll都是一个Assembly。Assembly具有以下几个特点:
- 一个Assembly可以包含一个或多个module和资源文件(resource file)
- 一个Assembly是一个可以单独并重复使用的最小单位,module不是
- 一个Assembly必然带有一个清单文件(manifest)
- 一个Assembly附带有一个版本号
让我们来一个个看看这些特点都说明了什么。这里老白将使用迅雷断点续传的例子来给大家讲解,虽然并不完全一致,或者对应并不准确,但希望通过这种方式能够方便大家理解。
一个Assembly可以包含一个或多个module和资源文件(resource file)
这说明了Assembly是module的一个超集。他不仅包含了最重要的主体,也就是各种类型(这些类型都被封装成了module),还包含了资源文件。这些资源文件可以是任意文件,比如jpg图片,或者excel数据表等等。这使得我们可以将一些数据和逻辑代码隔离开来,而不需要将这部分信息也变成代码放进类型中。
其实这个很常见,我们使用迅雷进行大的压缩文件下载的时候,可能压缩文件除了我们需要下载的(比如视频),还有另外一个私货比如广告文件或者图片。Assembly就类似这个压缩文件,视频就是我们的主体module,而广告或者图片就是我们说到的资源文件。
一个Assembly是一个可以单独并重复使用的最小单位,module不是
也就是说,在C#的运行或者使用过程中,我们其实是没有办法单独操作一个module的。我们只能够操作或者运行一个Assembly。也就是说,module脱离了Assembly的存在是没有意义的。我相信很多聪明的同学已经想到了其中一个例子来印证,就是反射。如果大家有印象,我们使用反射的时候用的是Assembly.GetType来获得某个类型的,从来就没有使用到module这个概念。
我们在使用迅雷下载的时候也是,断点续传就是把一个大的文件分成了很多小的部分。我们可以认为这些小的部分就是module。当我们单独拿到一个压缩文件的某一小部分的时候,我相信大家都有经验,这一小部分我们几乎是无法使用的(之所以说是几乎,因为有小概率一小部分包含了完整的信息)。只有我们把所有这些小部分合在一起组成完整的文件我们才能正常使用。Assembly和module的关系也就类似于此。
一个Assembly必然带有一个清单文件(manifest)
这个可以说是区分一个Assembly和module的一个决定性的标志。一个Assembly必然会有一个manifest文件。这个文件中声明了一些比较重要的信息,比如这个Assembly中包含了哪些文件(module或者资源文件),这个Assembly包含了哪些public的可以被其他Assembly引用的成员等等。
这个清单文件可以包含在某一个已有的module中,也可以是单独的不包含任何信息的新module。
这个manifest就很像我们使用迅雷下载的时候,我们会提前知道我们下载的包含了哪些内容或者文件。如果没有这么一个清单,那么我们想下载一个马拉多纳停球集锦,但是下载的文件并不告诉我们他包含了马拉多纳的集锦,那么我们只能下载完成后打开看了才知道是什么内容。这样就非常没有效率。因此有了manifest,其他程序就知道这个Assembly有什么信息,他们就能够根据需求决定是否使用,而不需要一一检测Assembly的内容。
一个Assembly附带有一个版本号
这里其实有必要说明下C#经常会用到的几个版本号(神奇的设定)。
下图是一个典型的AssemblyInfo.cs的配置,里面包含了两个version,一个是AssemblyVersion,还有一个是AssemblyFileVersion,有些时候还可能看到AssemblyInformationVersion。那到底这三个version有什么关系呢?
- AssemblyFileVersion:这个信息主要是给人看,CLR并不会检验这个版本。这个信息可以在Assembly的文件属性中找到。这个version主要目的是帮助开发人员对用户系统中的Assembly进行问题排查。
- AssemblyInformationalVersion:这个信息和AssemblyFileVersion一样,都主要是给人看的,CLR本身并不会进行校验。这个信息也同样可以在文件属性中找到。这个version一个主要目的是声明这个Assembly是在产品的哪个版本中被使用的。
- AssemblyVersion:这个是我们说到的Assembly的版本号。这个信息会存储在manifest中。这个信息很重要,因为这个版本号是这个Assembly的唯一标识,当其他Assembly需要进行引用的时候,使用的就是这个AssemblyVersion进行引用。
下面是一个AssemblyInfo.cs的例子:
using System.Reflection;using System.Runtime.CompilerServices;using System.Runtime.InteropServices;[assembly: AssemblyVersion("1.2.3.4")][assembly: AssemblyFileVersion("2.3.4.5")][assembly: AssemblyInformationalVersion("3.4.5.6")]
然后可以使用理解C# 核心概念 – C# 模块(module) 介绍到的csc来进行编译打包:
csc.exe Program.cs AssemblyInfo.cs
这里我们又省略掉了/out参数和/t参数,因为/t的默认参数就是exe,默认也会使用Program的名字来生成exe文件。
然后我们就可以右键查看生成的Program.exe的属性,如下:
Program.exe文件属性
我们可以看到,之前提及到的AssemblyFileVersion和AssemblyInformationalVersion都在属性中进行了显示,但是AssemblyVersion却没有。
最后再提及一点这几个version,他们都是由4个数字和点号组成。其遵守的是[大版本号(major number)].[小版本号(minor number)].[编译版本号(build number)].[修订版本号(revision number)]。
如何生成Assembly
我想聪明的同学已经知道了,在上一讲理解C# 核心概念 – C# 模块(module)我们讲解到了如何生成module,但是其实我们也得到了一个Assembly,一个只有一个module的Assembly。
由于直接引入module文件会让大家非常难以理解,因此在上一篇中老白模糊了这两者之间的界线,告诉大家之前生成的Program.exe就是一个module。其实准确的说,Program.exe是一个只包含了一个module的Assembly。那么我们是不是有办法只生成module而不生成Assembly呢?答案是肯定的,只需要使用/t:module参数,我们就能够得到一个.netmodule的文件。这个文件就是一个纯module,在被组成Assembly之前这个module并不能被使用。
除了这个/t:module参数之外,其他的所有/t参数都会得到一个Assembly。现有的/t参数有:
- exe: 获得一个控制台应用程序
- winexe:具有UI的应用程序
- library:dll文件,无法直接运行,只能被其他应用或者程序引用
- appcontainerexe:只能在应用容器中运行的应用程序,这种类型生成的程序可以在微软的应用商店中运行。
- winmdobj:这个类型生成的.winmdobj文件只能被WinMDExp.exe进行消费
也就是如下命令,将是我们最常见的生成Assembly的命令:
csc.exe /t:exe [文件...]csc.exe /t:winexe [文件...]csc.exe /t:dll [文件...]
以上的命令都只是生成一个单module的Assembly。在下一篇中,老白将用示例展示如何生成多个module的Assembly以及包含资源文件的Assembly。
好了,到此,老白就将什么是Assembly以及如何生成介绍完了。如果有什么问题,欢迎大家和老白一起探讨。
码字不易,如果觉得有用或者喜欢老白的内容,欢迎点赞推荐收藏关注转发。