本文章我们将来重点介绍强命名程序集,强命名程序集的出现其实是为解决版本控制问题,比如说,在新版程序集发布后,我们希望在系统中对旧程序集的引用继续保留,而有些地方又可以引用新的程序集,再比如说不同的公司提供了不同功能的程序集,这些类库存放在一个公共目录,有时候可能会出现名称相同的情况。使用强命名程序集可以解决这些问题,一个强命名的程序集是靠公钥标示、程序集版本号、区域属性、程序集名称这四个属性来唯一标识的,这样一来,新发布的库文件版本与前面发布的不同,不同的版本引用可以在元数据里面标识,相互不会受到影响,而且在99.99%的情况下不同公司生成的程序集这四个属性不会全都一样,可以大大降低应用程序部署的风险。

一 强命名程序集的各个部分


版本号

[assembly: AssemblyVersion("1.0.0.2")]

众所周知,程序集的版本是通过程序集级别的定制Attribute来标识的,一个版本号总是包含4个部分,分别是 Major Version(主版本号),MinorVersion(次版本号),Build(构建),Revision(修订),可以按照自己的版本号规划自行指定。构建版本号或修订版本号可以自动生成,此时这里的AssemblyVersion属性值可以是1.0.0.*或1.0.*,当使用这种方式时,自动生成的Build为从2000-01-01起的天数,Revision为从凌晨00:00:00 起的秒数/2。我们举个简单的例子。

using System;
using System.Reflection;[assembly: AssemblyVersion("1.0.*")]
public class a{public static void Main(){DateTime dtBase=new DateTime(2000,1,1);DateTime dtWeeHours=new DateTime(DateTime.Now.Year,DateTime.Now.Month,DateTime.Now.Day,0,0,0);  Console.WriteLine("Build版本号:" + DateTime.Now.Subtract(dtBase).Days);        Console.WriteLine("Revision版本号:" + (int)DateTime.Now.Subtract(dtWeeHours).TotalSeconds / 2);                        }
}

运行之后的结果如下

dll的版本号如下(修订号相隔1是因为编译时间和运行时间不同,如果用版本号去反算生成时间,就不会有这个问题)

  

这样一来就很明确了,Build版本号对应到生成的日期,Revision对应生成的时间,只要两台机器的时间不出问题,自动生成的dll版本永不会重复。在实际项目中我们肯定是把版本信息(上面那段属性声明)放到一个单独的文件中,vs新建项目时会默认生成一个AssemblyInfo.cs文件,这个文件里面就包含这个属性,当然还包含了很多其他的内容,像上面截图中的产品版本,内部名称,文件版本等等,大家可以去了解下。

 区域性

[assembly: AssemblyCultureAttribute("en-Us")]

程序集的区域性是通过上面的定制Attribute来标识的,区域性可以用来标识程序集的语言,如果不指定,则认为是语言中性。为了让应用程序支持多语言,按照微软的官方提倡的做法,应该是把与语言文化有关的内容放到一个单独的资源文件中,分不同的语言提供多个不同的dll,不同的语言之间用区域性来标识,在主程序调用时通过反射的方式来加载(注意不是直接引用,这样一来可以更加方便的部署和发布)。正因为如此,如果某个程序集直接引用了一个区域性不为中性的dll,则会抛出一个警告“CS1607: 程序集生成 -- 引用的程序集“××××”是本地化附属程序集”。

区域性不为中性的程序集被称作附属程序集,可执行文件不能为附属程序集,如果标识的区域性不为空,则编译时会报错“ error CS0647: 发出“System.Reflection.AssemblyCultureAttribute”属性时出错 -- “可执行文件不能是附属程序集,区域性应始终为空””。 除此之外,在运行时加载附属程序集也会与语言中性程序集有所不同,这一点我们在后面应用程序配置中再做深入说明。

  公钥标记

<add assembly="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>

以上内容是从Web.Config中截取到的,最后的那一部分,PublicKeyToken=B77A5C561934E089,等号后面的那部分就是公钥标识,它其实是嵌入在程序集中公钥经过SHA-1散列运算得到的密文的最后8个字节。在继续讲公钥标记前,我们先来了解一下程序集生成时使用公钥/私钥对进行签名的一个过程。

1 生成一个公钥私钥对,这个公钥的大小可以指定

  sn -k d:\liuzh.snk

2 生成时使用keyfile开关指定密钥文件

  csc /out:d:\apple.dll /t:library /keyfile:d:\liuzh.snk d:\apple.cs

在使用/keyfile:开关后,在生成时编译器会做如下事情:
  1 对程序集FileDef清单中的各个文件进行hash运算(默认是SHA-1,使用AL.exe生成时可以用/algid来覆盖),再使用私钥(私钥来自于liuzh.snk)对该散列值进行加密,得到密文。

  2 将公钥(公钥也来自于liuzh.snk)和1中的密文嵌入到程序集中。

下图展示了这一过程

微软保证了任何情况下sn生成的公钥/私钥的内容都不会相同,也就是说,嵌入到程序集中的公钥都是唯一的,在引用该程序集时,假如在引用的程序集中记录下被引用的程序集的完整的公钥会使得文件特别的大(比如说,任何一个 程序集都包会含了对mscorlib.dll的引用,因为任何类都继承于Object)。下图展示了生成的程序集中的公钥

由于引用的程序集只能记录被引用程序集公钥散列运算后的部分内容,这样一来就存在一个问题,就是可能有两个程序集最后的公钥标识一致,但是概率会很低(小于1/264),加上程序集的唯一性还有其他内容来区分,所以这不会出现什么问题。

顺便提一下,进行过公钥/私钥签名的程序集可以从一定程序上防止篡改,在运行时CLR会用公钥将使用私钥加密后的密文进行解密,将解密后的密文与程序集中各模块hash后的散列进行对比,如果不一致,则可以判定被篡改了。之所以说是一定程序上,是因为这种保护机制很脆弱,这点我们在后面进行说明。

程序集的名称

程序集的名称默认就是/out开关指定的文件的名称(不带扩展名)。使用vs时,这个名称可以在项目属性中指定,正因为在生成时程序集名称与文件名保持一致,所以你在项目属性中指定的是什么名称,最后生成的dll,exe就会使什么名称

2 全局程序集缓存


如果一个程序集需要被多个应用程序访问(如微软提供的类库),那么必须把它放到一个已知的目录,而且CLR在检测到对该程序集的一个引用时,需要知道自动检查目录。这个已知的目录就叫做全局程序集缓存(Global Assembly Cache,GAC),放到GAC中的程序集称为共享程序集,因为它可以被其他应用程序共享,仅在程序运行目录下的,我们称之为私有程序集。对于.net 3.5 以前版本,这个目录位于C:\Windows\Assembly,对于.net4.0,它位于C:\Windows\Microsoft.NET\Assembly。

GAC的目录是结构化的,其中包含了许多子目录,并有一个算法来生成这些子目录,永远不要手动将程序集文件手动复制到GAC,相反,应该使用工具来完成,这个工具是GACUtil.exe,可以在命令行查看它的用法。它包含了比较常用的功能如安装,卸载,删除,重新安装,筛选等。注意,只能将一个强命名的程序集安装到GAC中,如果不是强命名(没有进行过签名),在安装时就会提示“将程序集添加到缓存失败:试图安装没有强名称的程序集”,在server2003下,这个文件夹是这样子的。

这个目录里面几乎包含了应用程序在运行时的所需要的所有程序集,在安装.net framework时,实际会安装程序集文件的两个拷贝,一套安装在编译目录,另一套安装在GAC中。之所以要安装两套,是因为在编译时CSC.exe并不会去GAC中寻找你引用的dll,为什么编译器不去GAC中寻找呢,是因为在编译时/reference必须要知道具体的路径,而GAC的目录是不公开的,一个可能的替代方案是在编译时指定程序集的强名称,如System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089,但是这种方案很明显都没有直接部署两套dll来得方便。

将程序集部署到GAC中是对程序集注册的一种手段,虽然没有记录到注册表中,但是很明显破坏了我们的一个基本目标,简单安装,备份,还原,移动和卸载,所以如果我们不是提供类库供他人使用的话,还是应该直接把dll放到程序运行目录下

3 小结


进一步了解程序集相关信息可以加强我们理解各种dll的引用方式及项目属性中的配置原理,.net安装目录下各种dll及实用程序的功能,以在程序编译或运行时,快速定位和解决问题。

转载于:https://www.cnblogs.com/Haiz/p/3828557.html

C#编译基础知识(三)相关推荐

  1. (六)JS基础知识三(走进作用域和闭包)【三座大山之二,不会闭包,基本不会通过】

    JS基础知识三(作用域和闭包) 提问 作用域 自由变量 闭包 this 提问 this的不同应用场景,如何取值 手写bind函数 实际开发中闭包的应用场景,举例说明 创建10个a标签,点击的时候弹出对 ...

  2. CSS基础知识---三种选择器

    CSS基础知识---三种选择器 选择器 标签选择器 id选择器 class选择器 全部代码 选择器 标签选择器 id选择器 class选择器 标签选择器 选择器优先级:id>class>标 ...

  3. 编译原理——编译基础知识

    编译基础知识 语言是什么 1.1,高级语言 语言就是一个记号系统 通过语法来组成语义 1.2,语法规则 如何语言程序可以看成一定字符集 语法使得这串字符串形成一个形式上正确的程序 1.3,词法规则 规 ...

  4. C# 基础知识 (三).主子对话框数值传递

    在C# winform编程中,我们经常会遇到不同窗口间需要传递数值的问题.比如数据库的应用,主窗口填写内容num1,点击按钮,在弹出的子窗口显示对应num1值;或者在子窗口填写新注册用户名信息,在主窗 ...

  5. 三校生计算机word基础知识,三校生计算机第一次月考计算机基础、word.doc

    三校生计算机第一次月考计算机基础.word 云南省高等职业技术教育招生考试试题 计算机基础.word基础知识(9月考试卷) 姓名:_ __ ____ 得分:____ ___ 一.单项选择题(在每小题给 ...

  6. Framebuffer基础知识(三十)

    1.Framebuffer应用编程   在Linux系统中通过Framebuffer驱动程序来控制LCD.Frame是帧的意思,buffer是缓冲的意思,这意味着Framebuffer就是一块内存,里 ...

  7. C语言基础知识(三)-程序设计结构、数组、字符串处理函数

    本文是C语言的基础知识,主要讲解三种程序设计结构.数组.字符串和字符数组.数组元素查询以及字符串处理函数. 程序结构设计 包括C语言在内的几乎任何编程语言都支持以下三种程序设计结构,它们分别是: 顺序 ...

  8. Dapper基础知识三

    在下刚毕业工作,之前实习有用到Dapper?这几天新项目想用上Dapper,在下比较菜鸟,这块只是个人对Dapper的一种总结. Dapper,当项目在开发的时候,在没有必要使用依赖注入的时候,如何做 ...

  9. Profinet协议基础知识(三)

    三.PROFINET IO设备 1.PROFINET IO设备类型 PROFINET IO设备可分为Device(从站).Controller(主站)与Supervisor(监视器). PROFINE ...

最新文章

  1. 视频|结构光3D相机光机核心技术及3D成像性能分析
  2. hdu 统计难题(map)
  3. ThinkPHP的field方法的用法总结
  4. Python面向对象进阶及类成员
  5. 如何把本地项目上传到Github上面(详细版)
  6. 初学Java Web(5)——cookie-session学习
  7. ubuntu 16.04 搭建 python 开发环境
  8. ASP网站实现防止被采集
  9. linux c变量命名规则,C语言中变量名及函数名的命名规则与驼峰命名法
  10. Unix环境高级编程学习笔记(二)
  11. 【转】Unity利用WWW http传输Json数据
  12. 三维点云学习(3)6- 实现K-Means
  13. Spring的注解 @Bean用法
  14. Ubuntu中的zip / unzip 和 rar / unrar 命令:压缩 / 解压 zip 和 rar 文件
  15. 使用Qt给微信头像添加国旗
  16. 天龙八部TLBB系列 - 网单服务端各目录文件说明【超详细】
  17. 【查看】 - 内网(局域网)ip 、公网(外网)ip - ipconfig 、 tracert
  18. oracle olap创建物化视图,CUUG oracle物化视图讲解
  19. 2015编程之美资格赛 回文子序列个数
  20. 计算机原理期末试卷,计算机组成原理 期末试卷七及答案

热门文章

  1. mysql实验总结范文_数据库实训心得
  2. 年轻人,请听我说……
  3. 配置免密登录报错:ssh: Could not resolve hostname note1: Name or service not known
  4. python3 日文截图翻译和实时翻译
  5. 安卓Android sqllite实现保存数据和读数据
  6. java字符串用0X0F分割_微信公众帐号开发教程第4篇-----开发模式启用及接口配置Java...
  7. 分析优酷/土豆/pptv/乐视 HTML5、m3u8地址
  8. 计算机工程本科旧金山找工,2020年旧金山大学本科热门专业
  9. 主機名稱控制者: DNS 伺服器
  10. Python各种包学习