C#中的命名空间与Java和Python中的导入

14 投票
6 回答
6139 浏览
提问于 2025-04-16 04:27

在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 个回答

3

1) 你说得对。一开始确实没有“直接”的方法来知道你的类是从哪里来的,但正如你所说的,你可以在开发环境中跳转到它。不过,这种声明类的方式只是最简单的做法。如果你想要更清楚地知道你的 Bar 类是从 Foo 类来的,你可以这样声明:

private foo.Bar myBar = new foo.Bar();

这样一来,你一眼就能看出你的类是从哪里来的。

2) 当你添加一个类的引用时,添加引用的窗口会给你提供你想要的信息。如果你想在声明之后知道它们的来源,可以打开一个叫“解决方案资源管理器”的窗口,在“引用”这个树节点下你可以找到这些信息。你可以把它设置为始终可见(默认就是这样)。

3

对于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'
8

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 文件,里面包含了特定命名空间的所有委托声明(而不是有一堆单独声明的文件),但现在这已经不是什么大问题了。

撰写回答