看来您正在选择重载“名称空间”和“模块”的术语。当事物不符合您的定义时,您认为事物是“间接的”也就不足为奇了。
在大多数支持名称空间的语言(包括C#)中,名称空间不是模块。命名空间是定义名称的一种方式。模块是范围行为的一种方式。
通常,.Net运行时支持模块的概念(与隐式使用的模块定义稍有不同),但很少使用。我只看到它在SharpDevelop中构建的项目中使用,主要是这样,您可以从使用不同语言构建的模块构建单个DLL。相反,我们使用动态链接库来构建库。
在C#中,只要名称空间都在同一个二进制文件中,它们就可以在没有任何“间接层”的情况下进行解析。不需要考虑的任何间接调用都是编译器和链接器的责任。一旦开始构建具有多个依赖项的项目,就可以引用外部库。一旦您的项目引用了外部库(DLL),编译器就会为您找到它。
在Scheme中,如果需要加载外部库,则必须先做类似的事情(#%require (lib "mylib.ss"))
,或者像我记得的那样直接使用外部函数接口。如果您使用的是外部二进制文件,则需要花费相同的工作量来解析外部二进制文件。可能是您最常用的库是如此常用,以至于有一个基于Scheme的shim将从您那里抽象出来,但是如果您不得不编写自己的与3rd party库的集成,则实际上您需要做一些工作来“加载” “ 图书馆。
在Ruby中,模块,命名空间和文件名的连接实际上比您想象的要少得多。LOAD_PATH使事情变得有些复杂,并且模块声明可以在任何地方。Python可能更接近以您在Scheme中看到的方式来做事,除了C中的3rd party库仍然会增加(小的)折痕。
此外,诸如Ruby,Python和Lisp之类的动态类型语言通常没有与静态类型语言相同的“合同”方法。在动态类型的语言中,通常只建立一种“绅士协议”,代码将对某些方法做出响应,并且如果您的类似乎在讲相同的语言,则一切都很好。静态类型语言具有在编译时强制执行这些规则的其他机制。在C#中,使用这样的协定至少可以为您提供遵守这些接口的适度有用的保证,这可以使您将插件和替换捆绑在一起,并在某种程度上保证其通用性,因为您都可以针对同一协定进行编译。在Ruby或Scheme中,您可以通过编写在运行时可以运行的测试来验证这些协议。
这些编译时间保证带来了可衡量的性能优势,因为方法调用不需要双重调度。为了在Lisp,Ruby,JavaScript或其他地方获得这些好处,现在仍需要在专用VM中进行即时静态编译类的机制,这种机制现在仍然有些奇怪。
C#生态系统仍然对相对不成熟的支持是对这些二进制依赖项的管理。Java已经使用Maven来解决确保拥有所有必要依赖项的问题,而C#仍然具有相当原始的类似于MAKE的方法,该方法涉及在策略上提前将文件放置在正确的位置。