本文是为了对程序集和托管模块加深理解而整理的资料,希望大家看了此篇文章后对程序集和托管模块的概念更清楚一点。 如果你正在开发面向DotNet平台的应用程序,那么你肯定对“程序集”和“托管模块”这两个概念不陌生,这是DotNet带来的术语。这两个概念很容易混淆,有人认为它们指的是同一样事物,其实不然。这里,我写下自己的一些理解。
为了便于说明问题,我们先看看一个公司的某项目开发团队,这个“团队”由团队“成员”组成(可能还有一些资源),而在这些团队成员中,必然有一个成员是这个团队的头头,为了表示不是普通的成员,头头都有一个称号,比如说“经理”或者“负责人”。经常的,说到某个团队,我们只要知道它的头头就可以了。也就是说,头头代表了这个团队(注意,这里是"代表")。如果其他人有什么事情要与这个团队交流(比如团队向外说明本团队完成了什么任务等),没有必要将每个成员找来交流,我们只要找到头头就可以了。因为头头知道本团队的一切,他掌握了关于团队的所有信息:本团队有哪些成员,而且有些什么资源可以利用,与其他哪些团队有交流等等。常见的团队有多个成员,一个成员的团队是不多见的。但为了说明问题,我们假定一个成员的团队和多个成员的团队都存在。
从上面的讨论中我们可以这样总结:团队是个逻辑概念,并不是指某个人或资源,它是一个集合,而且这个集合不为空,只有当拥有大于或者等于多个成员的时候它才称之为团队。
嘿,大家看出来了,我之所以要说项目开发团队和成员,是因为在DotNet中的程序集和托管模块的概念与此类似。程序集其实并不是说某个文件,它是一个逻辑概念,就像一个团队。当然,我们习惯说a.exe 或者b.dll是个程序集,其实这样说多少会让人混淆,请看下面的说明。而托管模块就像是团队中的团队成员。哈,你肯定想到了,其中必然有一个托管模块是整个“团队”的“头头”,它负责管理所有的托管模块,关于这个程序集的一些信息也保存在这个托管模块中。我们把这样的模块叫做“主托管模块”(我记得某本书上是这样叫的)。为了区分主托管模块和普通托管模块,怎么办呢?我们给普通托管模块和主托管模块不同的后缀名,普通托管模块的后缀名是.netmodule,而主托管模块的后缀名是.exe 或者.dll。和开发团队中头头代表整个团队一样,主托管模块代表着整个程序集。既然是主托管模块,那么它肯定与其他托管模块不一样。一般的托管模块包含有IL代码和元数据,而这个主托管模块有没有IL代码和元数据并不重要,但它一定要有“清单”,也就是关于整个程序集的数据。
为什么主托管模块会有两种后缀名呢?这是因为分工的原因,有的程序集是为其他程序集提供便利的,它实现了一些数据类型(类库)或者拥有某些资源――这就是后缀名为.dll的程序集。这样的程序集不喜欢抛头露面,它们是“幕后程序集”。另一种程序集,后缀名为.exe,用户直接和它们打交道。有的功能它自己实现,但有些呢就交给幕后程序集去实现。为了区别,我们就把这样的程序集叫做“前台程序集”吧。DotNet规范中没有这两个概念,这里是为了理解才造的词。当然,读者朋友都会有自己比较习惯的理解名字。一个可执行应用程序中,只有一个前台程序集的概念,但是可以有零个或一个或多个幕后程序集。
我们又回到实际生活中的开发团队概念上来。团队的组成有这样几种情况:
一个成员,这样的成员是比较厉害的那种,比如个人软件开发者,自己对自己负责,自己是自己的头头;
多个成员,其中某个成员是头头;
程序集与此类似,所以就有了单模块和多模块程序集(有时也称单文件程序集和多文件程序集)。如果要实现的功能不是很烦杂,那么就用单模块程序集吧;但是,如果要实现的功能比较多,而且也好分开,那么建议你用多模块程序集;如果以后某个功能的实现方案改变的话,只要修改这个模块就行了,这样一来,极大的降低了开发的繁杂度。还有几点要注意,有的托管模块并不实现某种计算功能,它们仅仅是提供一些资源,比如说字符串;有时程序集除了包含托管模块外,还包含一些另外的资源文件。
我相信现在大家对程序集和托管模块分别是什么以及两者间的关系有了较好的理解。但是如果有源代码辅助一下那就更好了。是的。对程序员来说,源代码比什么都亲切。好的,下面就举两个简单的例子(用C#语言表述)。一个是单模块程序集,一个是多模块程序集。两者都是前台程序集(后缀名是exe)。我用Visual C# 2003 集成开发环境试了一下,竟然发现它不支持多程序集的开发(希望是我没有找到)。没关系,我们还有DotNet FrameWork SDK呢,不用怕。它自带的C#编译器csc.exe很好用。至于csc的用法我就不多说了。
例子1。单模块程序集:
首先找个文本编辑器,将下面的代码敲进去,然后将它保存起来,取名为hello.cs。例如我把它存为e: esthello.cs。 using System;
namespace nsApp
{
public class CEnterPoint
{
static void Main()
{
Console.WriteLine("Hello, verybody!");
}
}
}
然后,打开SDK命令行提示,定位在hello.cs所在的文件夹,比如我的是e: est。在命令行提示中输入命令 csc hello.cs,这个命令默认的是生成 .exe文件,也就是一个主托管模块。现在到hello.cs所在的文件夹中看看,你会发现一个新文件hello.exe。好,一个主托管模块诞生了。既然有主托管模块,那么就标志着一个程序集的诞生。再看看,没有其他的托管模块,那么这个程序集就是一个单模块程序集。
例子2。多模块程序集:
假设我们要编一个程序来管理动物园。这里为了简单,不考虑继承等问题,每个动物用一个类来表示,并且用一个托管模块来实现。将所有的动物都放在命名空间nsZoo中。现在动物园有两种动物:Dog和Cat。看下面的代码:
using System;
namespace nsZoo
{
public class Dog
{
public void SayHello()
{
Console.WriteLine("I am a dog, wang wang wang");
}
}
}
using System;
namespace nsZoo
{
public class Cat
{
public void SayHello()
{
Console.WriteLine("I am a cat, miao miao miao");
}
}
}
using System; using nsZoo;
namespace nsApp
{
class CEnterPoint
{
static void Main(string[] args)
{
Dog aDog = new Dog();
Cat aCat = new Cat();
aDog.SayHello();
aCat.SayHello();
Console.ReadLine();
}
}
}
将上面的三个文件保存好,比如放在文件夹e:zoo 中。再一次打开SDK命令行提示,定位到e:zoo 输入命令 csc /t:module Dog.cs 看看文件夹,你会发现一个新文件 Dog.netmodule 哈,这是一个托管模块,普通的托管模块。接着输入命令 csc /t:module Cat.cs 同样得到一个普通托管模块 Cat.netmodule 。好现在有了两个普通托管模块。 为了实现一个程序集的梦想,必须还要有一个主托管模块。好,接着输入csc /addmodule:Dog.netmodule;Cat.netmodule Main.cs 你会在文件夹中发现一个名为Main.exe的文件,这就是你想要的主托管模块。
好了。现在你有三个托管模块,并且任命其中一个为主托管模块,这样你就拥有了一个程序集了。这个程序集的组成是:Dog.netmodule, Cat.netmodule, Main.exe 。我们习惯说这个程序集是Main.exe 。但是一定要知道,Main.exe 其实是一个程序集的“头头”,由它代表着整个程序集。