C#中的命名空间与Java和Python中的导入
在Java和Python的世界里,你看一个源文件就能知道所有的导入是从哪里来的(也就是说,你知道被导入的类是在哪个文件中定义的)。举个例子:
在Java中:
import javafoo.Bar;
public class MyClass {
private Bar myBar = new Bar();
}
你立刻就能看到Bar类是从javafoo导入的。所以,Bar是在/javafoo/Bar.java
这个文件中定义的。
在Python中:
import pythonbaz
from pythonfoo import Bar
my_bar = Bar()
my_other = pythonbaz.Other()
这里很明显,Bar是来自pythonfoo这个包,而Other显然是来自pythonbaz。
在C#中(如果我说错了请纠正我):
using foo
using baz
using anothernamespace
...
public class MyClass
{
private Bar myBar = new Bar();
}
有两个问题:
1) 我怎么知道Bar类是在哪儿定义的?它是来自命名空间foo
,还是bar
,或者anothernamespace
?(编辑:不使用Visual Studio)
2) 在Java中,包名和目录名是对应的(或者说这是一个很强的约定)。因此,当你看到一个类来自哪个包时,你就知道它在文件系统中的目录位置。
而在C#中,似乎没有这样的命名空间约定,还是我漏掉了什么?那么,在不使用Visual Studio的情况下,我怎么知道该去哪个目录和文件找呢?(在搞清楚类来自哪个命名空间之后)。
编辑说明:我知道Python和/或Java允许使用通配符导入,但这些语言的“文化”是不太提倡这样做的(至少在Python中是这样,在Java中我不太确定)。另外,在Java的IDE中,通常会帮助你创建最小的导入(正如Mchl在下面评论的那样)。
6 个回答
1) 你说得对。一开始确实没有“直接”的方法来知道你的类是从哪里来的,但正如你所说的,你可以在开发环境中跳转到它。不过,这种声明类的方式只是最简单的做法。如果你想要更清楚地知道你的 Bar 类是从 Foo 类来的,你可以这样声明:
private foo.Bar myBar = new foo.Bar();
这样一来,你一眼就能看出你的类是从哪里来的。
2) 当你添加一个类的引用时,添加引用的窗口会给你提供你想要的信息。如果你想在声明之后知道它们的来源,可以打开一个叫“解决方案资源管理器”的窗口,在“引用”这个树节点下你可以找到这些信息。你可以把它设置为始终可见(默认就是这样)。
对于Java和Python来说,这确实是个约定的问题——你应该导入你需要的类,而不是用通配符导入整个包。
在C#中,你不能只对你想要的特定类使用using
指令,因为它只适用于命名空间(下面的错误信息就说明了这一点)。看起来C#保持了C++中命名空间的概念,并将其与#include
指令结合起来,提供了一种简单的方式来引用外部类。
using System.Net.Sockets.Socket; // Gives the following error: // A using namespace directive can only be applied to namespaces; // 'System.Net.Sockets.Socket' is a type not a namespace
至于双重Bar
声明,这很简单——如果编译器无法判断,就会报错:
using Foo; // Has class Bar {} using Goo; // Has class Bar {} Bar b = new Bar(); // Gives the following error: // 'Bar' is an ambiguous reference between 'Foo.Bar' and 'Goo.Bar'
1) 好吧,你在Java中也可以做到同样的事情:
import java.util.*;
import java.io.*;
...
InputStream x = ...;
InputStream
是来自 java.util
还是 java.io
呢?当然,你可以选择不使用这个功能。
现在,从理论上来说,我明白这意味着当你用文本编辑器查看代码时,你无法判断C#中的类型来源……但实际上,我觉得这并不是个问题。你有多少次真的在看代码时不能使用Visual Studio呢?
2) 当然,你在.NET中也可以使用相同的约定——我也是这样做的,尽管我没有空目录向上延伸……所以如果我创建一个默认命名空间为X.Y的项目,那么 X.Y.Foo
就会在 Foo.cs
中,而 X.Y.Z.Bar
会在 Z\Bar.cs
中。
这也是Visual Studio默认的做法——如果你创建一个子文件夹,它会根据项目的默认命名空间和文件夹结构创建新的类。
当然,你也可以在任何旧文件中声明类型——但大多数人会遵循正常的约定,用对应的文件名来声明类型。在泛型使得委托声明变得不那么常见之前,我曾经有一个 Delegates.cs
文件,里面包含了特定命名空间的所有委托声明(而不是有一堆单独声明的文件),但现在这已经不是什么大问题了。