.Net与程序集

一个简单的C#程序

回想一下我们第一个.net 程序 hello world,它具有那些步骤呢

  1. 打开visual studio

  1. 创建一个C# console的项目

  1. build

  1. 运行程序

这时候就有一个命令行窗口弹出来,上面打印着hello world。我们打开文件夹的bin目录,会发现里面多了一个.dll文件和一个.exe文件。我们用dotnet run命令就可以运行起来这个程序。

过去在使用VC++生成的可执行文件,经过了预编译,编译,汇编,链接这几步以后,生成了Native code,这样在操作系统和本地机器指令集的支持下,程序就开始运行了。C#作为高级语言,而且是跨平台的语言,它是如何才能让操作系统认识它的呢?

CIL/IL-公共中间语言

Java会先把java代码转化为字节码,然后再通过JVM中的编译器JIT编译成本地代码后运行的。

C#也是一样的 c#通过C#编译器编译为IL代码,然后经过JIT编译为本地代码。

IL代码和字节码是类似的。

我们可以通过dnSpy工具查看.dll文件的内容,这就能直观的看到IL代码。

IL代码只是比C#低级一些,也不能直接被CPU执行,IL语言需要.Net运行时环境的支持,执行前会被JIT(Just-in-time)的二次编译,才能转变为计算机可以识别的指令。因此IL代码也叫做托管代码。不需要.net 运行时就可以直接运行的代码叫做非托管代码。

BCL

我们在写Console.Wirte()这个方法时,会疑惑这个方法从何而来。我们打开Visual stdio,查看一个程序的Dependencies, 就会发现里面引入了System.Console这个类。并且无法删除。

BCL就是提供了像Console这样的类型来支持开发者编写,也就是基础类库。

FCL

这是.Net的框架类库,BCL是FCL的一个子集。

BCL提供基元类型, 集合类型, 线程处理, AppDomain, Runtime等程序最为基本的封装。

然后要提供类似网络,文件等对操作系统的封装。

最后是Windows Form, Asp .net, WCF这种大型框架。

CTS

CTS定义了一套语言可以做什么,不能做什么,是一套规则。满足了这个规则的语言就是面向.NET框架的语言。比如C#有类,对象,枚举,结构体,委托,方法,事件,访问性(Private, public, protected),assembly等等。

CLS

首先提出一个问题,用VB写的程序集,可以被c#项目引用吗?

C#和VB可以很好的运行在.net框架下,但是他们是无法相互引用的,如果要让C#项目能够引用VB,那么VB中的公开类型就要满足C#的语言特性,就是说要有共通之处。

世界上有很多种语言,所以我们需要一个规范,只要满足这个规范,就能被其他语言的程序集所使用。这个规范就是CLS

CLR

CLR有时也会称作.NET运行时。

.dll和.exe有个别名叫做程序集。

在windows环境下,.exe给人的感觉就是双击以后就开始运行,.NET程序首先要是一个windows可执行程序。Windows想要加载.dll或者.exe是因为它可以理解PE/COFF文件格式,也就是windows可移植可执行/通用对象文件格式。

程序集中的IL代码不能直接被机器执行,还需要即时编译,编译前需要将编译的环境运行起来,所以PE/COFF头之后就是CLR头,CLR头告诉操作系统这是.NET程序集,区别其他的可执行文件。

接下来就是清单,清单相当于一个目录,描述程序集名称,版本,程序集包含的Resources, 组成程序集的文件。

下面是元数据,清单描述了程序集自身信息, 元数据描述了程序集所包含的内容,比如类型,类型成员, 类型和类型成员的可见性。元数据不包含类型实现,查看元数据的过程就是反射。

下面是程序代码,就是IL代码。

最后是资源文件,比如一些html, css, js ,jpg

这里简单的介绍了一下程序集。

CLR就是一个软件层,管理.NET程序集的执行,提供管理应用程序域,加载运行程序集,安全检查,将CIL代码即时编译为机器代码、异常处理、对象析构和垃圾回收等。CLR还有一种叫法,即VES(Virtual Execution System,虚拟执行系统),我觉得这个描述更加的准确, 类似于java虚拟机。

如何运行一个程序集

  1. 操作系统在尝试打开一个托管程序集(.exe)时,首先会检查PE头,根据PE头来创建合适的进程。

  1. 接下来会进一步检查是否存在CLR头,如果存在,就会立即载入MsCorEE.dll。这个库文件是.NET框架的核心组件之一,注意它也不是一个程序集。

  1. 加载了MsCorEE.dll之后,会调用其中的_CorExeMain()函数,该函数会加载合适版本的CLR。

  1. 在CLR运行之后,程序的执行权就交给了CLR。CLR会找到程序的入口点,通常是Main()方法,然后执行它。这里又包含了以下过程:

  1. 加载类型。

  1. 验证。

  1. 即时编译。

  1. 加载类型: 在执行main()方法以前,Class loader要找到拥有Main()方法的类型(Program.cs 中的 Program类)并且加载,会从配置文件,程序集元数据中找到这个类型,然后把类型信息加载到内存,并且缓存。并给每个方法插入存根(stub)。

  1. 验证:保证代码类型安全,主要就是看元数据和类型签名。

  1. 即时编译:把托管代码IL编译为机器代码。即时编译只有方法第一次调用时发生。在调用方法时,CLR会检查方法的存根,如果存根为空,则执行JIT编译过程,并将该方法被编译后的本地机器代码地址写入到方法存根中。当第二次对同一方法进行调用时,会再次检查这个存根,如果发现其保存了本地机器代码的地址,则直接跳转到本地机器代码进行执行,无须再次进行JIT编译。

程序集

程序集主要包括程序集模块,程序集资源和强名称程序集。

程序集模块

这是一个中间的逻辑结构,程序集可以包含一个或者多个模块,每个模块的内容就是PE/coff, CLR头,清单,元数据,CIL代码, 资源文件。这个现在没怎么用了,visual studio也都是生成的单模块程序集。

程序集资源

程序集中包含资源, 资源可以是字符串,也可以是一些文件, 比如图片和Excel。

强名称程序集

特点和功能:

  1. 唯一标识一个程序集。

  1. 防止程序集被仿冒和被篡改。

  1. 可以部署到全局程序集缓存(GAC)之中。

举个没有强名称程序集的例子:

假设有一个类库ClassLib, 一个控制台项目ConsoleApp想要引用ClassLib,如果ClassLib.dll是非强名称的程序集,那只能私有部署。通过项目引用或者Reference引用,本质上就是复制一份ClassLib.dll副本到ConsoleAPP的根目录下。

这里有两个属性分别是“复制本地”和“路径,开启复制本地时,编译时会自动复制ClassLib.dll到应用程序根目录。路径指的就是程序集的位置。

如果没有强程序集,有三个ConsoleApp都要引用这个ClassLib, 但是ConsoleApp1, ConsoleApp2, ConsoleApp3都不是同时开发的,而且ClassLib也在维护中。本来ConsoleApp1要引用版本1的ClassLib现在成了版本3了,ClassLib的版本1和3又不兼容,就错误了。

如果你想用其他公司开发的ClassLib.dll,那你的dll就会被冲突掉,因为Window系统的同一个文件夹下,是通过文件名来区分不同文件的,文件名称相同就会被覆盖。

如果没有强程序集会遇到什么问题?

版本冲突:如果多个程序集具有相同的名称和版本号,但是公钥不同,运行时会被视为不同的程序集,导致版本冲突。

安全性: 没有强命名的程序集可能被恶意软件篡改。

部署问题: 如果想在多个计算机上用程序集,那就必须是强程序集,否则无法工作。

强名称和GAC就是来解决这个问题的。

强名称:定义一个规则,不单单以文件名区分程序集。

GAC: 特殊文件夹,可以识别强名称的规则,允许相同的文件名存在。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值