在上一篇中,老白讲解了什么是Assembly,以及如何生成Assembly。在这一篇中,老白将讲解以下内容:
- 为什么需要Assembly
- 示例
- 生成多个module的Assembly
- 添加资源文件到Assembly
通过这两部分的学习,我相信同学们对Assembly会有更进一步的感知,对Assembly和module的区别也会有进一步理解。
为什么需要Assembly老白相信有很多同学和老白一样,有这么一个疑问,为什么已经有了module还需要Assembly呢?或者说为什么需要这种类似于两层组合(一层是module,Assembly是包装着module的第二层)的结构呢?我们能不能把现有的Assembly的功能归到module中,然后把Assembly去掉呢?
如果单单从C#的角度出发,我们或许可以这么做(但是依然会丢失一些功能),但是如果我们从CLR的角度出发(如果还记得,Assembly和module都是CLR的概念,非C#独有),那么这种双层结构确实一种必然。
- 我们知道CLR全称是Common Language Runtime,既然是Common那就不是C#独有的,我们就可以使用CLR来运行使用任何适配CLR的语言,比如C#,VB,F#等等。这就允许我们一个系统的不同模块(此模块非CLR的模块,是一个逻辑上的概念)甚至不同的子操作都可以使用不同的语言来完成,但最后打包成一个单独的文件。比如,我们可以某些类型使用C#,这会产生一个module1,另外一些功能使用F#来完成,这会产生一个module2。然后可以使用工具将module1和module2放入同一个Assembly中。当这个Assembly被其他应用使用的时候,他们并不会意识到这个Assembly是由多个语言共同编写的。如果没有这种两层结构,这个功能就无法满足。
- 我们在上一节谈到,Assembly是可以包含资源文件的。这就允许我们将一些数据或者配置放置到单独的文件中。单看这一点,我们似乎是可以将两层结构进行合并的,直接让module文件可以包含资源文件而不仅仅是中间代码。但是,从隔离的角度来看,单层结构并不是一个好主意。现在的模型,每个module都是纯粹的编译过的中间代码,如果按照OOP的说法就是一个类只干一件事情,如果我们将资源文件也混入module中,这就显得有些混乱。(而且其实本质来说,将资源文件归入module也是两层。module是一层,源码和资源文件是一层)
- 双层模型允许我们进行部分下载。假设我们有一个Assembly,包含了module1(包含manifest清单文件),module2和module3。当我们在本地运行程序的时候引用到了Assembly中的类型,CLR并不会将这三个module全部下载,而是先下载带有manifest清单文件的module,在这个例子中就是module1。然后CLR会查看manifest,程序需要的类型是在哪个module,如果是在module1中,那么module2和module3将不会在被下载。如果类型是在module2中,那么module2会再被下载,但是module3依然不会被下载。如果只是单层结构,那么这种部分下载的能力必然将缺失。
从上面提到的三个理由中,我们可以看到,这种module Assembly的结构能够帮助我们很好的实现类型和资源的分类。
多module的Assembly示例既然Assembly可以允许包含多个module以及资源文件,接下来我们就看看如何生成这样的Assembly。
生成多个module的Assembly
时至今日(2020年12月了),Visual Studio依然不能支持生成多个module的Assembly。因此以下的所有操作都是通过命令行完成的。
我们先编写两个文件,Free.cs代表可以免费使用的功能,Paid.cs代表需要付费使用的功能。
Free.cs文件如下:
public sealed class Free{
public static void Run() {
System.Console.WriteLine("免费使用");
}
}
Paid.cs文件如下:
public sealed class Paid{
public static void Run() {
System.Console.WriteLine("付费使用");
}
}
Program.cs文件如下:
public sealed class Program{
public static void Main() {
System.Console.WriteLine("hello world");
System.Console.WriteLine("========Free Function========");
Free.Run();
System.Console.WriteLine("========Charged Function========");
Paid.Run();
}
}
准备好了以上三个文件之后,运行如下命令可以生成一个单独的module(仅仅是一个module不是Assembly,因此不能被单独运行):
csc.exe /t:module Paid.cs
运行完上述命令之后,将会生成一个Paid.netmodule文件。有了这个module文件之后我们就可以创建包含多个(这个例子中是两个module)module的Assembly了,运行如下命令:
csc.exe /out:ProgramMultiModule.exe /addmodule:Paid.netmodule Program.cs Free.cs
以上命令将会生成一个类似于之前Program.exe的ProgramMultiModule.exe。只不过这个ProgramMultiModule.exe包含了两个module。/addmodule参数就是告诉编译器,Paid.netmodule需要被包含在生成的Assembly中。Paid.netmodule的相关信息也会被包含在Assembly的清单文件manifest中。
接下来,我们可以通过运行ProgramMultiModule.exe来确认Paid.netmodule被正确的包含了,整个运行结果如下:
添加资源文件到Assembly
由于有两种不同的添加资源文件的方式,这里我们准备两个文件resource.txt和resource1.txt。
resource.txt文件的内容如下:
嵌入到Assembly文件中的文字
resource1.txt文件的内容如下:
Assembly关联文件中的文字
然后将我们之前的Program.cs文件稍做改动,最后如下:
public sealed class Program{
public static void Main() {
System.Console.WriteLine("hello world");
System.Console.WriteLine("========Free Function========");
Free.Run();
System.Console.WriteLine("========Charged Function========");
Paid.Run();
System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
System.IO.Stream stream = assembly.GetManifestResourceStream("resource.txt");
System.IO.Stream stream1 = assembly.GetManifestResourceStream("resource1.txt");
using(System.IO.StreamReader sr = new System.IO.StreamReader(stream))
{
System.Console.WriteLine("========Reading from embedded file=======");
string line;
while((line = sr.ReadLine()) != null)
{
System.Console.WriteLine(line);
}
}
using(System.IO.StreamReader sr = new System.IO.StreamReader(stream1))
{
System.Console.WriteLine("========Reading from external link file=======");
string line;
while((line = sr.ReadLine()) != null)
{
System.Console.WriteLine(line);
}
}
}
}
改动后的Program.cs将会输出两个资源文件中的内容。然后我们可以运行如下命令来添加资源文件,生成ProgramMultiModuleWithResource.exe文件:
csc.exe /out:ProgramMultiModuleWithResource.exe /addmodule:Paid.netmodule /resource:resource.txt /linkresource:resource1.txt Program.cs Free.cs
细心的同学已经发现了,在这个命令中,我们增加了两个参数resource和linkresource。这两个参数都是将资源文件添加到Assembly中,区别就是resource参数会将资源文件包含到Assembly中(也就是最后只有一个Assembly文件,不需要单独的资源文件)。而linkresource参数只是进行一个关联,告诉Assembly需要这么一个文件,最后进行发布的时候除了需要发布Assembly也需要同时发布这个关联的资源文件。
命令运行结束之后,我们就可以双击运行ProgramMultiModuleWithResource.exe了。如果一切顺利的话,将会打印出两个resource文件里的内容。
输出资源文件内容
为了验证,我们的resource.txt文件已经被包含到了Assembly中,我们可以将刚刚的resource.txt文件删除,然后再次运行ProgramMultiModuleWithResource.exe,应该会得到和刚刚一样的结果。
删除内嵌资源文件,运行程序依然输出资源文件内容
而如果我们将resource1.txt删除,当我们再次运行ProgramMultiModuleWithResource.exe的时候,我们就会得到错误。这也验证了我们之前解释的两种添加资源文件的差别。
删除关联资源文件,运行程序出错
好了,到此,老白就将如何生成包含多个module的Assembly以及如何添加资源文件做了简单介绍。联合 ,也就把Assembly的大概介绍完了。如果有什么问题,欢迎大家和老白一起探讨。
在下一篇,老白打算给大家介绍下Assembly的国际化使用,欢迎到时阅读。
码字不易,如果觉得有用或者喜欢老白的内容,欢迎点赞推荐收藏关注转发。