各位老铁们,大家好,今天由我来为大家分享net文学网站源码分享,以及文学网站简介的相关问题知识,希望对大家有所帮助。如果可以帮助到大家,还望关注收藏下本站,您的支持是我们最大的动力,谢谢大家了哈,下面我们开始吧!
本文来自『.NET大牛之路』体系专栏的免费分享,追更完整专栏请私撩我……
上一篇我们介绍了Roslyn编译器,我们知道,我们编写的C代码经过编译会生成.dll或.exe文件,这些文件就是.NET的程序集(Assembly)。
尽管.NET的程序集文件与非托管的Windows二进制文件采用相同的文件扩展名(*.dll),但它们的内部完全不同。具体来说,.NETCore程序集文件不包含平台(泛指操作系统和CPU架构的组合)特定的指令,而是平台无关的中间语言(IL)和类型元数据。
你可能在一些.NET/.NETCore的文档中看到过IL的另外两种缩写:MSIL(MicrosoftIntermediateLanguage,微软中间语言)和CIL(CommonIntermediateLanguage,通用中间语言)。IL、MSIL和CIL都是一个概念,其中MSIL是早期的叫法,现在已经很少有人用了。
但.NETCore与.NETFramework不一样,.NETCore始终只会生成*.dll格式的程序集文件,即使是像控制台应用这样的可执行项目也不再会生成*.exe格式的程序集文件。
那我们在.NETCore项目的bin目录中看到和项目同名的*.exe文件是怎么回事呢?这个文件并不是一个程序集文件,而是专门为Windows平台生成的一个可执行的快捷方式。在Windows平台双击这个文件等同于执行dotnet<assemblyname>.dll命令。在我们安装的.NETCore目录中有个dotnet.exe命令文件(如Windows系统默认位置是C:\\ProgramFiles\\dotnet\\dotnet.exe),在编译时,该文件会被复制到构建目录,并重命名为与项目名称同名的<assemblyname>.exe文件。
程序集的组成
总的来说,每个程序集文件主要由IL代码、元数据(Metadata)、清单(Manifest)和资源文件(如jpg、html、txt等)组成。其中,IL代码和元数据会先被编译为一个或多个托管模块,然后托管模块和资源文件会被合并成程序集。
托管模块,或者叫托管资源或托管代码,顾名思义,这种资源是由.NETCore的CLR运行时来管理运行的,它包含IL代码和元数据。比如对象的回收是由CLR中垃圾回收器(GC)自动执行的,不需要手动管理。
程序集文件中占比最大的一般是IL代码。IL代码和Java字节码相似,它不包含平台特定的指令,它只在必要的时候被.NETCore运行时中的JIT编译器编译成本机代码(机器码)。
程序集文件中的元数据详细地描述了程序集文件中每个类型的特征。比如有一个名为Product的类,类型元数据描述了Product的基类、实现的接口(如果有的话)和每个成员的完整描述等细节。元数据由语言编译器(Roslyn)自动生成。
除了托管模块,程序集文件还可以嵌入资源文件,如jpg、gif、html等格式的静态文件,这些文件是非托管资源。
当托管模块和资源文件合并成程序集时,会生成一份清单,它是专门用来描述程序集本身的元数据。清单包含程序集的当前版本信息、本地化信息(用于本地化字符串等),以及正确执行所需的所有外部引用程序集列表等。
在第5篇文章中我们讲了.NET的两种执行模型,其中,当基于本地运行时执行模型发布时,虽然你的应用程序可以发布为可直接执行的单一文件,但这个单一的文件其实是多个文件的包装。它包含了由IL代码编译成的本地代码和NativeAOT本地运行时。你的代码仍然在一个托管的容器中运行,运行时它的资源的管理和它作为多个文件发布是一样的。
下面让我们更详细地了解一下IL代码、元数据和程序集清单。
IL代码
我们先来看看下面这样一段简单的C代码如下:
classCalculator\n{\npublicintAdd(intnum1,intnum2)\n{\nreturnnum1+num2;\n}\n}
经过编译后,在项目的bin\\Debug目录会生成一个与项目名称同名的dll程序集文件。我们使用ildasm.exe工具打开这个文件,定位到Calculator的Add方法,可以看到Add方法的IL代码如下:
.methodpublichidebysig\ninstanceint32Add(\nint32num1,\nint32num2\n)cilmanaged\n{\n//Codesize9(0x9)\n.maxstack2\n.localsinit(\n[0]int32\n)\n\nIL_0000:nop\nIL_0001:ldarg.1\nIL_0002:ldarg.2\nIL_0003:add\nIL_0004:stloc.0\nIL_0005:br.sIL_0007\n\nIL_0007:ldloc.0\nIL_0008:ret\n}
以我的安装环境为例,你可以在这个位置找到ildasm.exe工具:C:\\ProgramFiles(x86)\\MicrosoftSDKs\\Windows\\v10.0A\\bin\\NETFX4.8Tools\\ildasm.exe。为了使用方便,你可以把该工具配置到VisualStudio的外部工具中。
这就是IL代码的样子,如果使用VB或F2(02000003)\n——————————————————-\nTypDefName:ConsoleApp.Calculator(02000003)\nFlags:[NotPublic][AutoLayout][Class][AnsiClass][BeforeFieldInit](00100000)\nExtends:0100000C[TypeRef]System.Object\nMethod1:I4\nArgument编译器本身所使用。元数据是众多.NETCore技术的支柱,比如反射、对象序列化等。
程序集清单
.NETCore程序集还包含描述程序集本身的元数据,我们称之为清单。清单记录了当前程序集正常运行所需的所有外部程序集、程序集的版本号、版权信息等等。与类型元数据一样,生成程序集清单也是由编译器的工作。
同样地,还是以上面Calculator类所在项目为例,我们也来看看程序集清单长什么样子。在ildasm.exe工具打开的程序集的目录树中,双击MAINFEST即可查看程序集的清单内容:
.assemblyexternSystem.Runtime\n{\n.publickeytoken=(B03F5F7F11D50A3A)//.?_….:\n.ver5:0:0:0\n}\n.assemblyexternSystem.Console\n{\n.publickeytoken=(B03F5F7F11D50A3A)//.?_….:\n.ver5:0:0:0\n}\n.assemblyConsoleApp\n{\n…\n.custominstancevoid…TargetFrameworkAttribute…\n.custominstancevoid…AssemblyCompanyAttribute…\n…\n.hashalgorithm0x00008004\n.ver1:0:0:0\n}\n.moduleConsoleApp.dll\n.imagebase0x00400000\n.filealignment0x00000200\n.stackreserve0x00100000\n.subsystem0x0003//WINDOWS_CUI\n.corflags0x00000001//ILONLY
可以看到,程序集清单首先通过.assemblyextern指令记录了它所引用的外部程序集。接着是当前程序集本身的信息,记录了程序集本身的各种特征,如版本号、模块名称等。
提前编译IL代码
前面提到,IL代码需要先通过JIT编译器编译成特定平台的本地代码,才能在该平台运行。你可能会问,.NET为什么要将源代码编译成IL代码,而不直接编译成特定平台的本地代码呢?
这样做主要有两个好处:一是语言整合,一套运行时环境可以运行多种语言编写的程序,.NET团队不用开发和维护多套运行时;二是平台无关,方便程序和库的移植,编译后的程序集可以发布到多个平台,而不用为不同的平台发布特定的程序文件。虽然IL代码带来了可移植性等的好处,但需要以牺牲一点点启动时的性能作为代价。
一般我们的Web应用程序最终只会部署在一种平台(如Linuxx64),为了更快的启动性能,在启动时,我们确实可以不需要中间语言编译这个环节,省去启动时的JIT编译的时间。.NETCore为我们提供了两种方式把IL代码提前编译成特定平台的本地代码。
一种方式是使用ReadyToRun功能。.NETCore运行时(CoreCLR)中的一个叫做CrossGen的工具,它可以预先将IL代码编译成本地代码。要使用这个功能,只需在程序发布的时候,选择特定平台,在发布选项中勾选EnableReadyToRuncompilation即可。不过ReadyToRun功能目前只适用于Windows系统。
另一种方式是使用.NET5新增加的AOT编译功能。发布时选择Self-Contained模式,发布后生成单个文件。AOT编译也是提前将IL代码编译成本地代码,不同的是,它在发布时生成的单个文件还包含一个精简版的本地运行时。这点在第5篇文章讲过,不再累述了。
这两种方式都有弊端,第一种目前只适用于Windows系统,第二种Self-Contained单个文件发布要比多文件发布大几十M。不过对于第一次启动慢那么一点点(可能甚至不到一秒的时间),大部分的Web应用程序都是完全可以接受的。如果实在对启动时性能有严格的要求,也可以使用预热的方案。
小结
本文介绍了程序集以及它的内部组成:IL代码、元数据、资源文件和程序集清单。总的来说,程序集就是.NETCore在编译后生成的*.dll文件,它包含托管模块、资源文件和程序集清单,其中托管模块由IL代码和元数据组成。
需要强调的是,IL代码不包含特定平台的指令,它只在需要的时候才会被CoreCLR运行时中的JIT编译器编译成特定于平台的本地代码。
通过本文,相信大家对.NETCore程序集和它的内部组成已经有了一个整体的认识。
如果你还想了解更多这方面的信息,记得收藏关注本站。