Assembly是.net中的基本部署单元,也是所有类型的容器。程序集包含编译类型及其 IL(中间语言)代码、运行时资源和信息,以助于版本控制、安全和引用其他程序集。程序集定义了类型解析和安全许可的边界。

一般来说,一个程序集包括一个单一的Windows可移植执行文件(PE)--如果是一个应用程序,扩展名为.exe,如果是一个可重用的库,扩展名为.dll。WinRT库的扩展名是.winmd,与.dll类似,只是它只包含元数据,没有IL代码。

本节的大多数类型来自下面几个命名空间:

System.Reflection

System.Resources

System.Globalization

Assembly具体是什么

一个Assembly包含四类文件(数据):

  • 程序集清单(assembly manifest):

向.net运行时提供的信息,比如程序集的名称,版本,要求的许可,以及它引用的其他程序集。

  • 应用程序清单(An application manifest):

向操作系统提供的信息,比如程序集应该如何被部署,是否需要管理员权限。

  • 已编译的类型(Compiled types):

已编译的 IL 代码和程序集中定义的类型的元数据

  • 资源(Resources)

程序集重点其他数据,比如图片和本地文本。

只有程序集清单(assembly manifest)是必须的,尽管一个程序集几乎总是包含编译过的类型(除非它是一个WinRT参考程序集)。

无论是可执行文件还是库,程序集的结构都是相似的。这与可执行文件的主要区别在于它定义了一个入口点。

程序集清单(The Assembly Manifest)

程序集清单有两个目的:

  • 向托管主机环境描述程序集
  • 它充当程序集中模块(modules)、类型(types)和资源(resources)的目录

因此,程序集是自描述的。使用者以发现一个程序集的所有数据、类型和函数,而不需要额外的文件。

程序集清单不需要你手动添加到程序集,它作为编译的一部分自动嵌入到程序集中的

清单内容

以下是清单中存储的具有重要功能的数据的

  • 程序集的简单名称
  • 一个版本号(AssemblyVersion)
  • 程序集的公钥和签名哈希(如果有强命名)
  • 引用程序集的列表,包括它们的版本和公钥
  • 程序集中定义的类型列表
  • 它所针对的文化(culture),如果是附属程序集 (AssemblyCulture)

清单还可以存储以下信息数据

  • 程序集的完整的标题和描述(AssemblyTitle 和 AssemblyDescription)
  • 公司和版权信息(AssemblyCompany 和 Assembly

版权)

  • 显示版本(AssemblyInformationalVersion)
  • 自定义数据的附加属性

其中一些数据来自提供给编译器的参数,例如引用程序集的列表或用于签署程序集的公钥。其余来自程序集属性,在括号中表示

查看清单内容

您可以使用 .NET 工具 ildasm.exe 查看程序集清单的内容。也可以使用反射以编程方式执行相同的操作

设置程序集属性

常用的程序集属性可以在 Visual Studio 中项目的属性页上的包选项卡(Package tab)上指定。该包选项卡(Package tab)上的设置将被添加到项目文件 (.csproj)。

如果要指定包选项卡不支持的属性,或者如果不使用.csproj 文件,您可以在源代码中指定程序集属性。

.NET Framework项目会在Properties目录自动创建一个文件AssemblyInfo.cs,但 .NET Core 项目没有。

尽管你可以在项目中的任何源代码文件中指定属性,但专门为属性添加一个.cs文件,可以让你把它们放在一起,并且组织得很好。

一个专门的属性文件只包含using语句和汇编属性的声明。例如,为了向一个单元测试项目暴露内部范围的类型。

你应该这样做:

using System.Runtime.CompilerServices;
[assembly:InternalsVisibleTo("MyUnitTestProject")]

应用程序清单(Windows)

应用程序清单是一个XML文件,用于向操作系统传递关于组件的信息。在构建过程中,应用程序清单作为 Win32 资源嵌入到启动可执行文件中。如果清单文件存在,清单会在CLR加载程序集之前被读取和处理,并且会影响Windows启动应用程序进程的方式。

一个.NET应用清单在XML名称空间中具有一个名为assembly的根元素

urn:schemas-microsoft-com:asm.v1:

<?xml version="1.0" encoding="utf-8"?> 
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1"> 
  <!-- contents of manifest --> 
</assembly>

下面的清单指示操作系统请求管理权限提升:

<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
	<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
		<security>
			<requestedPrivileges>
				<requestedExecutionLevel level="requireAdministrator" />
			</requestedPrivileges>
		</security>	
  </trustInfo>
</assembly>

部署应用程序清单

您可以通过以下方式将应用程序清单添加到 Visual Studio 中的 .NET Core 项目:

在解决方案资源管理器中右键单击您的项目,

选择添加,

然后选择“新建项目”,

然后选择应用程序清单文件

构建后,清单将嵌入到输出程序集中。

.NET 工具 ildasm.exe对嵌入式应用程序清单的存在“视而不见”。但是,如果您在解决方案资源管理器中双击程序集,Visual Studio 会指示是否存在嵌入式应用程序清单

模块(Modules)

程序集的内容实际上打包在一个称为模块的中间容器中。一个模块对应于一个包含程序集内容的文件。

之所以有这个额外的容器层,是为了允许一个程序集跨越多个文件,这个功能在.NETFramework 中存在,但在.NET核心中没有。

assembly详解_开发语言

尽管 .NET Core 不支持多文件程序集,但有时您需要了解模块带来的额外容器级别。主要场景是有反射的。

装配类(The Assembly Class)

System.Reflection 中的 Assembly 类是在运行时访问程序集元数据的入口(gateway)。

有多种方法可以获得程序集对象:最简单的是通过 Type 的 Assembly 属性

Assembly a = typeof (Program).Assembly;

您还可以通过调用 Assembly 的静态方法之一来获取 Assembly 对象:

GetExecutingAssembly

返回定义当前执行函数的类型的程序集。

GetCallingAssembly

与 GetExecutingAssembly 相同,但用于调用当前正在执行的函数的函数。

GetEntryAssembly

返回定义应用程序原始入口方法的程序集。

拥有 Assembly 对象后,您可以使用其属性和方法来查询程序集的元数据并反射(reflect)其类型

强名称和程序集签名

强命名程序集具有唯一标识。它通过向清单(manifest)添加两位元数据来实现:

  • 属于程序集作者的唯一编号
  • 程序集的签名哈希,证明唯一编号持有者生成了程序集

这需要公钥/私钥对。公钥提供唯一标识号,私钥便于签名。

公钥在保证程序集引用的唯一性方面很有价值:一个强命名的程序集将公钥合并到它的身份中。

强命名在 .NET Core 中不那么重要,因为 .NET Core 没有全局程序集缓存;也不会限制程序集被其他强命名程序集引用。

如何给Assembly强命名,这里先不讲了,因为在.net core中的作用不大。

程序集名称(Assembly Names)

一个程序集的唯一标识包含来自其清单(manifest)中的四个元数据:

  • 它的简单名称(simple name)
  • 版本号(默认是"0.0.0.0")
  • Its culture (“neutral” if not a satellite)
  • 它的公钥令牌(如果没有强命名则为“null”)

这个简单的名字不是来自任何属性,而是来自它最初被编译的文件名(减去任何扩展名)。

因此,System.Xml.dll 程序集的简单名称是“System.Xml”。

重命名文件不会更改程序集的简单名称

版本号来自 AssemblyVersion 属性。它是一个字符串,分为以下四个部分

major.minor.build.revision

您可以如下所示指定版本号:

[assembly: AssemblyVersion ("2.5.6.7")]

区域性来自 AssemblyCulture 属性并应用于附属程序集

The culture comes from the AssemblyCulture attribute and applies to satellite assemblies

正如我们在上一节中讨论的那样,公钥令牌来自编译时提供的强名称

全限定名(Fully Qualified Names)

完全限定的程序集名称是一个包含所有四个标识组件的字符串,格式如下

simple-name, Version=version, Culture=culture, PublicKeyToken=public-key

比如全限定名System.Private.CoreLib.dll 他的简单名称是System.Private.CoreLib,然后Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e。

如上所述,如果有的属性没有值则会被赋为默认值,或为空。

Assembly 对象的 FullName 属性返回其完全限定名称。在清单(manifest)中记录程序集引用时,编译器始终使用完全限定名称

完全限定的程序集名称不包含有助于在磁盘上定位它的目录路径。定位驻留在另一个目录中的程序集是完全不同的事情。