c# 获取命名空间下的所有类,
包与名称空间
众所周知,可重用性是软件工程中一个非常重要的目标。复用,不仅意味着软件(代码、组件等。)自己写的可以重用;更广义地说,重用是指不同的人、不同的团队、不同的公司可以利用别人的成果。此外,大型软件往往由多个团队开发,这些团队可能分布在不同的城市、地区甚至国家。由于这些原因,名称管理成为一个非常重要的因素。
由于C语言本身没有提供名称管理的机制(C语言的静态命名解决了可见性问题,这些名称不会导出到外部,但是我们要讨论的命名空间和这个问题并不完全一样),为了解决名称冲突的问题,人们一般选择前缀法;而前缀规则往往是:$ { projectname } _ $ { name }更安全的命名规则将前缀分为更多级别:$ { project name } _ $ { modulename } _ $ { name }。
现实生活中类似这种方案的例子还有很多。比如中国很多城市都有滨河路。如果你交谈的所有人都知道你所指的城市,你只需要说滨江路,所有人都会明白你所指的城市。但如果不是这样,就需要加个前缀表示这是乐山的滨江路还是成都的滨江路。这一点在你邮寄信件的时候体现的最为直接。
所以如果有全局管理的话,C语言的这个方案应该是很有效的。但它的缺点是可能会产生很长的名字,每次引用名字都要给出全名。
这是一件很麻烦的事情。想象一下你每次说起“中国四川省乐山市滨河路”的时候会有多痛苦。
为了解决这类问题,给出了有效的管理方案。后续的编程语言,无论是C、Java还是C#,都提供了自己的名字管理机制。这些方案在本质上都有自己统一的思路,但在操作模式上有所区别。
在C语言之前的方案中,体现了分级管理。分级管理是一种非常自然有效的手段。比如互联网的域名。它通过分层命名保证了一个名字的全局唯一性,排列从小范围到大范围(这是因为西方的书写习惯和方便。其实从这一点可以发现,如果一个人的读前习惯是从左到右,从小到大的排列非常方便节省时间,比如“中国四川省乐山市滨河路”。当我们已经从左边的信息中知道我们的意思时,我们可以跳过或忽略下面的信息。而且由大到小的排列可以避免错误,因为我们先知道了限制条件,最后在读滨河路的时候就已经确定了自己的意思)。我们可以在前面加一个名字,指定一个更小的范围。例如,wsd.wmsg.sps.motorola.com解释说,这是摩托罗拉SPS部门wmsg部门的wsd小组。
Java用这种方法命名Package,但是写法反过来。这种方法可以有效地保证命名的统一性。比如一个名为mlca的项目的mmi模块包可以命名为:com . Motorola . SPs . wmsg . wsd . mlca . MMI,而其引擎模块包可以命名为:com . Motorola . SPs . wmsg . wsd . mlca . engine。
这样,当不同团队、不同公司的代码放在一起使用时,我们只需要简单地使用一个名字,不会发生冲突。当有冲突时,我们只需指定它的全名。例如,上面的两个包都有一个名为Message的类。如果我们另一个包中的一个类想要同时使用这两个包,那么在引用消息类时,我们需要指明它来自哪个包。如下所示:
导入com . Motorola . SPs . wmsg . wsd . mlca . MMI;
导入com . Motorola . SPs . wmsg . wsd . mlca . engine;
//我们需要指明消息来自哪个包类。
public class Foo extends com . Motorola . SPs . wmsg . mlca . mlca . MMI . message {
.
}
C和C#提供了命名空间的概念来支持这种方法。可以在全局空间指定自己的命名空间,然后在某个命名空间做一个更小的命名空间。虽然C和C#本身并不推荐任何命名空间的命名方法(其实反域名的方法也是Java推荐的,并不强制),但是我们也可以使用上面的方法。例如,下面的C#代码:
命名空间com . Motorola . SPs . wmsg . wsd . mlca . MMI
{
//MMI的东西
.
}
命名空间com . Motorola . SPs . wmsg . wsd . mlca . engine
{
//引擎的东西
.
}
当我们同时使用这两个模块时,如果存在名称冲突,可能会通过指定名称空间来指示。例如:
class Foo:com . Motorola . SPs . wmsg . wsd . mlca . MMI . message
{
.
}
Java包本身没有子包的概念,所有包都是并行关系,不存在谁包含谁的问题。比如org.dominoo.action和org.dominoo.action.asl之间绝对没有包与子包的关系,它们是独立的包,每个包都有自己的类/接口集合。在org.dominoo.action.asl的java文件中,如果要引用org.dominoo.action中的类/接口,必须导入org.dominoo.action
C /C#的命名空间方案是不同的。一个名称空间可以有自己的子名称空间。我们不妨称之为命名空间包,这样在C /C#中包与子包之间就可能有关系。例如:
命名空间org.dominoo
{
命名空间操作
{
命名空间asl
{
.
}
}
命名空间约束
{
名称空间ocl
{
.
}
}
}
在这个例子中,action和constraint都是org.dominoo的子包,它们有自己的子包asl和ocl。
因此,对于一个全局命名空间,C语言不能直接分离命名空间,而Java可以从全局命名空间中分离出独立的命名空间,但C /C#可以进一步分离每个命名空间。如下图:
-
全局名称空间
-
c编程语言
-
全局名称空间
- - -
A B C
- - -
-
java 3d
-
全局名称空间
- -
A B
- - -
C D E
- - -
- -
-
C /C#语言
所以Java的包方案只划分一次全局命名空间。本质上,它只是为语言提供了一个命名前缀方案,只通过对命名前缀的分级管理来保证名称的唯一性。它唯一的功能是避免名称冲突。
C /C#提供了细分任何空间的能力。在Java中,org.dominoo和org.dominoo.asl是完全没有关系的独立包,但在C /C#中,dominoo.asl显然是dominoo的一个子包。
事实上,如果仅仅为了避免命名冲突而不需要C /C#这样的复杂方案,那么Java方案就足够了。但是C /C#方案可以带来其他便利:
1.软件开发的本质是自顶向下的分解。每一层都有自己的定义,这个定义可以作为下一层所有子系统的公共服务。多级树形结构符合这个逻辑。C /C#方案以最自然的方式满足了这种划分关系。其实这个方案的思路和文档管理的思路是一样的。
2.一旦一个程序使用了哪个名称空间,它就可以访问它的子包,而不用指出整个路径。比如上图,如果一个程序用命名空间A写,那么在访问C包中的类Foo时,只需要写C:Foo,而不用写完整路径:A:C:Foo。在Java中,由于A和C并置,为了访问C中的内容,必须明确指出导入C。然后,在访问Foo导致名称冲突的情况下,必须指出完整路径。
3.当程序在一个包中时,它可以直接访问外部包中的定义,而不会发生名称冲突。因为Java包只有一个层次结构,所以Java只能直接访问全局命名空间中的定义,其他包中的任何定义都只能通过导入来访问。
毫无疑问,C /C#的方案更强大、更灵活,但也更复杂。而复杂的东西往往会让用户更容易犯错。孰优孰劣,你可以自己判断。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。