为什么“using System;”不被视为不良实践?

57
我有一定的C++背景,我完全理解并同意这个问题的答案:为什么“using namespace std;”被认为是不良实践? 因此,我很惊讶的发现,在使用C#后,完全相反的情况发生了: using Some.Namespace; 几乎随处可见。每当你开始使用一个类型时,你首先会添加它所在命名空间的 using 指令(如果还不存在)。我不能回忆起看到过不以using System; using System.Collections.Generic; using X.Y.Z; etc... 开始的 .cs 文件。 事实上,即使你可能根本不需要它们,如果你通过 Visual Studio 向项目中添加新文件,它也会自动在那里添加一些 using 指示符。所以,在 C++ 社区中你会受到基本上的谴责,而 C# 却鼓励这样做。至少在我看来是这样的。
现在,我明白了 C# 和 C++ 中的 using 指示符并不完全相同。此外,我也明白,使用using namespace在 C++ 中最糟糕的事情之一,即将它放在头文件中,在 C# 中没有等价的糟糕情况,这是由于缺乏头文件和#include的概念。然而,尽管它们存在差异,但在C#和C ++中使用指示符具有相同的目的,即不必一直输入Some.Namespace.SomeType(在C ++中为:: 而不是 . ),而只需输入 SomeType 。但是出于同样的目的,危险似乎对我来说也是相同的:命名冲突。
在最好的情况下,这会导致编译错误,因此您只需要修复它。在最坏的情况下,它仍然可以编译,并且代码默默地执行与您打算执行的不同操作。因此,我的问题是:为什么在C#和C ++中显然使用指示符被认为是如此不平等的? 我有一些回答的想法(尽管没有一个真正让我满意):
  • C#中的命名空间通常比C ++中更长且更嵌套(std vs. System.Collection.Generic)。因此,在消除代码方面有更多的愿望和更大的收益。但即使如此,该论点仅适用于标准命名空间。自定义命名空间可以在C#和C ++中拥有任何短名称。

  • 在C#中,命名空间比C++更加“细粒度”。例如,在C++中,整个标准库都包含在std中(还有一些嵌套的小命名空间,如chrono),而在C#中,您有System.IOSystem.ThreadingSystem.Text等。因此,发生名称冲突的风险较小。但是,这只是一种直觉感觉。我没有实际计算过使用using namespace stdusing System导入了多少名称。而且,即使这是真的,在查看标准命名空间时才适用这个论点。无论是在C#还是C++中,您自己的命名空间都可以设计得像细粒度一样。

还有其他的论点吗?我特别关心实际的硬性事实(如果有的话),而不是那些意见。


2
@Timo,原帖明确表示涉及头文件污染问题。 - Konrad Rudolph
2
@ThomasWeller 在 C++ 中很明显。在 C# 中,可以考虑使用扩展方法 Ext(this T t, long l) 并通过 t.Ext(0) 调用它。如果您添加了另一个包含扩展方法 Ext(this T t, int i) 的命名空间,则会调用该方法。但我还不是 C# 的专家。 - sebrockm
1
@sebrockm 你的观点是正确的。即使这些扩展方法在不同的命名空间中定义,你也会得到它们的重载。不会生成任何警告。 - Timo
2
即使我承认你的观点,同样的论点也适用于C++,进一步推动了我对于C++和C#之间没有区别的疑问。 - sebrockm
3
@Franck 在 C++ 和 C# 中,如果存在歧义,编译器会抛出错误。 - Timo
显示剩余26条评论
2个回答

35
为什么“using System;”不被认为是一种不良实践?
“using System;”并非普遍不被认为是一种不良实践。例如,可以参考:Why would you not use the 'using' directive in C#? 但这也许是因为在C#中的使用情况通常没有using namespace std那么糟糕,可能的原因有:
  1. C#没有头文件。在预处理器中使用"include"一个C#源文件到另一个源文件中是不常见的。

  2. std命名空间是几乎平坦的,即几乎所有的标准库函数、类型和变量都在其中(有少数例外,如filesystem子命名空间)。它包含非常非常多的标识符。据我所知,System包含的名称要少得多,并且具有更多的子命名空间。

  3. 在C#中,没有全局函数或变量。因此,与C++相比,全局标识符的数量通常相当小。此外,使用C库(通常是间接地)是很典型的,而这些库没有命名空间,因此将它们所有的名称放入全局命名空间中。

据我所知,C#没有参数相关查找。 ADL结合名称隐藏、重载等可能会产生某些程序不受名称冲突影响的情况,而另一些程序则会受到微妙的影响,测试所有边角情况是不可行的。
因此,“using System;”比“using namespace std”更少发生名称冲突的机会。
此外,命名空间“导入”在某种程度上是一种自我维持的约定:如果惯例上导入标准命名空间,则程序员通常会试图避免为其自己的标识符选择该命名空间中的名称,这有助于减少此类约定的问题。
如果将这种导入视为不良惯例,则程序员将更不可能尝试避免与导入的命名空间发生冲突。因此,即使最初的选择之间的权重很微妙,惯例也趋向于支持或反对这种实践。

4
这只考虑了打开命名空间可能带来的潜在缺点。这是一个开始,但我认为我们还需要考虑优势:在C++中,在大多数情况下,它可以节省五个字符(std::)。在C#中,这个优势更大(System.仅有7个字符,但其他嵌套的命名空间有很多个字符,如果到处都写出来会使代码完全无法阅读)。 - Konrad Rudolph
C#的重载解析难道不比C++更友好,许多歧义都会通过编译器错误得到排除吗?(这绝不是对您权威回答的批评。) - Bathsheba
4
如果缺点被认为是重要的,并且键入量与 std:: 键入量相等,那么使用 using Sys = System; 而不是污染命名空间的非别名 using,是否会成为典型的风格? - eerorika
2
@Bathsheba 关于“权威性”,我想声明我对C#知之甚少,我的回答基于对C++的了解和对C#进行的一些谷歌搜索。因此,如果有人能核实一下C#部分,我将不胜感激 :) - eerorika
@eerorika 可能吧。但这只是又一个支持“使用”这个选项的论点,结合其他论点来看,“使用”这个选项更有优势。 - Konrad Rudolph
2
关于ADL部分:C#有扩展方法。这是一种不同的设计,但它们在命名冲突方面存在相同的问题。 - Timo

-2
然而,尽管它们有所不同,但 C#和 C ++ 中的 using 指令具有相同的目的,即只需始终键入 SomeType,而不是更长的 Some.Namespace.SomeType(在 C++ 中为 :: 而不是.)。基于这个相同的目的,也出现了危险:名称冲突。
是的,但您没有“导出”这种危险(即强迫他人处理它),因为:
现在,我确实理解在 C# 和 C++ 中使用指令并不完全相同。此外,我确实理解,在 C++ 中使用 namespace 的最困难的事情之一,即将其放在头文件中,由于缺少头文件和 #include 的概念,在 C# 中没有相应的功能。
所以它是一个不同类别的东西。
此外,C++ 不像 C# 那样“设计”以在 IDE 中开发。 C# 基本上总是在 Visual Studio 中编写,具有其 Intellisense 等功能。它被设计为由创建它的人使用。无论有多少人使用 IDE 在 C++ 中进行开发,它都不会将这种用例作为压倒性关注点来设计。
命名空间在C#中比C++更加“细粒度”。
没错,这点是没得说的。在C++中使用“using namespace std”和在C#中使用“using System.Collection.Generic”是无法相提并论的。
所以不要进行比较!

2
这并没有回答 OP 的问题,因为他明确关注的不是在头文件中打开命名空间,而是在实现文件中。 - Konrad Rudolph
2
@KonradRudolph 避免在C++中使用using namespace std的建议主要是关于头文件的。答案是,在C#中不适用此建议。 - Asteroids With Wings
9
避免使用using namespace std的建议显然不仅仅是关于头文件的。在头文件中使用它是危险的,在实现中使用也是不被建议的。 - Tommy Andersen
1
在C++中避免使用using namespace...的建议是为了避免命名冲突,而C#中的using指令也引入了可能存在命名冲突的可能性,那么为什么不避免它呢?即使它们的实现不同。 - Tommy Andersen
@TommyAndersen,如果出现这种冲突,解决起来有多难? 这只需要一秒钟。由于C#的using声明是每个定义单个类型/类的文件而定义的,并且该类型通常与相对狭窄的应用程序域概念相关(理想情况下,单一责任原则),因此出现这种名称空间冲突的可能性极低。但是如果发生了,很容易修复。常见的.NET开发工具/IDE会在很大程度上帮助您解决这些问题。考虑整个开发生态系统,它旨在通过结合多个开发活动的最佳部分使您更加高效。 - AKornich

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接