可以将程序集视为一个逻辑的DLL或EXE,一般由含有元数据的PE文件、IL代码文件和一些jpg、gif等资源文件构成。
**如何配置应用程序去下载程序集文件呢?**可以在应用程序配置文件中指定codeBase元素,在codeBase的URL中检查机器的下载缓存,如果有直接加载文件,没有的话去URL位置下载到缓存,如果URL都没有,直接抛出异常FileNotFoundException。
程序集的三大好处:增量下载、代码与资源文件分离、多语言封装
**注意:**对于常用到的那些设置和类型,虽然程序集是逻辑分组的,但您最好还是把它们放到一个文件里,这样可以让CLR缩短以下查找时间,提高下查找性能不是
##构建联系–元数据(清单表)
合并的前提就是清单,只有通过清单,CLR才能知道程序集的具体信息,知道该加载哪些资源文件。换句话说清单就是程序集的自我介绍。
也许各位看着表头疼,一如前例,我简单介绍一下这个自描述清单表的各部分到底有什么:
- AssemblyDef :程序集名称、版本、语言文化、一些标志、哈希算法、发布者公钥(特别注意,这里不分什么项,描述自身程序集的就这些内容)
- FileDef:每个PE和资源文件都有记录项,文件名和扩展名、哈希值、标志
- ManifestResoutceDef:主管资源,资源有两类,独立的资源文件和嵌入PE的资源流,对于独立的资源文件,给个指向FileDef的引用就行,如果是嵌入的,还得给个偏移量,确定资源在PE文件中的起止位置。当然还包括资源名称、一些标志(主要是访问标志)
- ExportedTypesDef:主管导出类型,所有PE模块中的每个导出类型都都应该有个记录项,包含类型名称,指向FileDef表中的引用以及指向TypeDef的引用。也就是定位到托管模块再定位到类型
##合并基本流程
###一些开关
以下几个常用开关可以帮助我们编译指定文件: /t:exe
生成控制台可执行文件 (/target:exe的缩写: )/t:winexe
生成图形界面可执行文件 (/target:exe的缩写: )/t:appcontainerexe
生成WindowsStore执行体 (/target:exe的缩写: )/t:library
生成类库执行体 (/target:exe的缩写: )/t:winmdobj
生成winmd执行体 (/target:exe的缩写: ) (不知道干嘛的 )
总之以上任何命令执行完都会生成程序集,换句话说,都包含清单文件、元数据PE+IL、资源文件(不一定有)三件套。可以直接让CLR操作,**但如果加了这个开关:/t:module
这样生成的就是一个托管模块了(不一定准确啊),反正就是不含清单文件,不能直接执行了。**要想执行这个模块,必须得把它加载到别的程序集里,使用这个命令/t:addmodule
。
###工作流程
####生成程序集文件
假定有两个源代码文件:Java.cs(包含不常用类型)、C#.cs(包含常用类型)。在有了之前的开关的基础上,这里同样用书中举的例子来说明情况吧:
1,首先编译一个非程序集模块:csc /t:module Java.cs
因为不常用,所以可以只编译为模块,用的时候再加载到程序集里去。
2,其次编译个常用的程序集模块,顺便再加上那个不常用的已经编译好的模块Java.netmodule
csc /out:C#biggerthanJava.dll /t:library /addmodule:Java.netmodule C#.cs
/t:addmodule
这个开关告诉编译器要将Java.netmodule文件添加到FileDef清单元数据表,并将Java.netmodule的公开导出类型添加到ExportTypesDefe清单元数据表,这儿没涉及到啥资源。和ManifestResoutceDef没啥关系。用图片表示就类似这样:
图片有些许错误哈,jeffrey也说明了,就像FUT,它是主模块,清单文件宿主,所以不应该出现自公开导出类型的。
这下可以在导出类型里看到引用token编号:0x26000001,引入题外话解释0x26啥意思:
0x26就是表示文件嘛,也就是表示引入文件的token号。
####加载程序集文件
经过以上各个步骤,程序集文件C#biggerthanJava.dll 已经生成了,要想用,当然得用命令引进来嘛,/r:C#biggerthanJava.dll
注意所有要引入的文件都必须存在!
- 方法首次调用,CLR检测作为参数、返回值或局部变量被引用的类型
- CLR试图加载所有引用的程序集中含有的清单文件,如果发现和1中检测到的类型一致的,则登记,总之要登记完所有1中指出的所有文件。注意,咱1中用不到的也就别加载了,浪费性能
##辅助神器–程序集链接器
这个东西的作用简而言之就是你先生成一堆module,用的时候它帮你链接成程序集,用这个方法,上述步骤就该这么来了:
csc /t:module Java.cs
csc /t:module C#.cs
al /out:C#biggerthanJava.dll /t:library Java.netmodule C#.netmodule
生成的程序集就如下所示了:
要是想加入资源文件(咱前边说过资源文件分独立和嵌入两种),也可以直接用命令:/embed
加嵌入的和/link
加独立的。
##程序集版本资源信息
###程序集版本号组成
通过一些命令或者C#编译器都可以指定程序集的版本信息:这些信息点击任何一个dll的属性都可见:
你可以通过命令(这里就不再详细说了,网上都可以查到)或者VS来直接标明:
这里介绍下版本号,个人感觉java和C#的版本号也很像:
简单来说一下(个人理解啊):拿微信举例吧,微信添加了朋友圈功能,这是大改,主板本号加,朋友圈添加了三天可见,这是小改,次版本号加,这两个是公众感受的。咱开发的时候,对于朋友圈三天可见这个dll每改一天生成一次,build加一,如果一天生成两次了,说明第一次生成有bug,咱改个bug,修订号review加一。
###程序集的三个版本号
- AssemblyFileVersion:简单理解就是文件每发生更新(主次版本号先设置好),版本号就加,机器自动加,windows资源管理器可见。
- AssemblyInformationalVersion:简单理解就是文件每发生更新(主次版本号先设置好),版本号就加,不同的是,内部和修订号人工打包标记
- AssemblyVersion:最重要的版本号!唯一标识程序集,存储在AssemblyDef清单元数据表里,也就是之前提到的清单元数据表里的版本号
##语言文化
不能每一种语言都开发一种代码吧,所以生成程序集应该指定为语言文化中性,然后把各种语言文化打包为附属程序集(专门放资源文件和语言文化这类不含代码的文件的),这样,用到的时候直接加载(使用/c:text指定,这里目录名称要符合语言文化,也就是上述那张表)就好:举个例子
#程序集的部署与控制
##.NetFrameWork部署目标
在COM时代(虽然根本没经历过COM时代),但凭使用感受说话吧:
1,安装一个应用程序导入了一个dll,一连串的报错,其它dll受到影响了(DLL HILL问题)
2,安装和卸载复杂,垃圾注册表根本清不干净。
3,安全性不高,用户不能完全控制。
部署目标也就是针对以上三个问题吧:解决DLL hell问题,不依赖注册表,将安全权限完全赋予给用户。
##简单应用程序部署
###对于windows Store的官方程序 - vs会将所有必要程序集打包为一个.appx文件,用户安装该文件时,所有程序集进入一个目录
- CLR从目录加载程序集,windows在桌面添加磁贴,若其它用户安装该appx,windows只添加磁铁(因为程序集已经在第一次加载好了呀)
- 删除时,如果有很多用户安装,只删不想要的用户的磁贴,如果都不想要,则删程序集。
###对于非官方开发者 - 只需要把全部代码复制粘贴到光盘里,所有引用的程序集都在里边,所以摆脱注册表了,这也许就是咱常说的离线安装包下载吧
- 用cab格式,打包成msi文件,联网按需下载。这也许就是在线安装吧
##简单管理控制
###管理程序集文件
其实主要是CLR定位和加载用的,假定发布者想把应用程序中一个程序集文件(MutiFileLibrary.dll)放到其它地方(也就是不在该应用程序所包含的程序集文件下),那么就需要配置文件:
结构类似这样,这样就可以通过Program.exe.config找到所有文件
该配置文件指明了,要去AuxFile子目录去查找,通过probing元素的privatePath特性。CLR先从应用程序基目录查找,找不到再按照配置路径**(配置路径都应为相对基目录的路径)**找。通常网站的配置文件就是Web.confg
###管理CLR
通过Machine.config来管理CLR,每一个CLR均对应一个Machine.config
全文到这里就结束了,整篇博文所讲的是**模块是如何生成,程序集又是如何生成的,CLR是如何加载程序集的(简述)。**完成的过程中也在思考一个问题,微软的元数据结构似乎是优于java的class文件结构的,微软的版本号策略似乎也比java的结构清晰。但为何如今发展起来这么缓慢,究其原因,可能就是之前没开源吧,果然开源改变世界啊。最近也一直在思考职业规划,如果要在C#这条路上前进,无疑得使用微软全套(虽然很爽但窄),现在搞的比较好的Azure云势头似乎正在追赶亚马逊的Aws,未来的开发真的会迎来云编译时代么?感觉前景较好的是.netcore和uwp编程以及基于.netFramework的Azure。