提问人:benPearce 提问时间:9/24/2008 最后编辑:Muhammad AsadullahbenPearce 更新时间:5/15/2023 访问量:275739
“using”指令应该在 C# 中的命名空间内部还是外部?
Should 'using' directives be inside or outside the namespace in C#?
答:
将它放在命名空间中会使声明成为该文件的该命名空间的本地声明(如果文件中有多个命名空间),但如果每个文件只有一个命名空间,那么无论它们是在命名空间外部还是内部都没有太大区别。
using ThisNamespace.IsImported.InAllNamespaces.Here;
namespace Namespace1
{
using ThisNamespace.IsImported.InNamespace1.AndNamespace2;
namespace Namespace2
{
using ThisNamespace.IsImported.InJustNamespace2;
}
}
namespace Namespace3
{
using ThisNamespace.IsImported.InJustNamespace3;
}
评论
using
namespace
namespace
根据 Hanselman - Using Directive and Assembly Loading... 和其他此类文章,在技术上没有区别。
我的偏好是将它们放在命名空间之外。
评论
My style is to put them outside the namespaces.
两者之间实际上有一个(微妙的)区别。假设您在 File1.cs 中有以下代码:
// File1.cs
using System;
namespace Outer.Inner
{
class Foo
{
static void Bar()
{
double d = Math.PI;
}
}
}
现在假设有人将另一个文件 (File2.cs) 添加到项目中,如下所示:
// File2.cs
namespace Outer
{
class Math
{
}
}
编译器在查看命名空间外部的这些指令之前会进行搜索,因此它会找到而不是 .不幸的是(或者也许是幸运的?)没有成员,所以 File1 现在被破坏了。Outer
using
Outer.Math
System.Math
Outer.Math
PI
如果将 放入命名空间声明中,则会更改,如下所示:using
// File1b.cs
namespace Outer.Inner
{
using System;
class Foo
{
static void Bar()
{
double d = Math.PI;
}
}
}
现在编译器在搜索之前进行搜索,找到 ,一切都很好。System
Outer
System.Math
有些人会争辩说,对于用户定义的类来说,这可能是一个不好的名字,因为 ;这里的重点是存在差异,它会影响代码的可维护性。Math
System
同样有趣的是,如果 is in namespace 而不是 .在这种情况下,添加 File2 会破坏 File1,而不管文件转到哪里。这意味着编译器在查看任何指令之前会搜索最里面的封闭命名空间。Foo
Outer
Outer.Inner
Outer.Math
using
using
评论
using directives must come first in the file
根据 StyleCop 文档:
SA1200:UsingDirectivesMustBePlacedWithinNamespace
原因 C# using 指令位于命名空间元素的外部。
规则说明 当 using 指令或 using-alias 指令放置在命名空间元素之外时,将发生违反此规则的情况,除非该文件不包含任何命名空间元素。
例如,以下代码将导致两次违反此规则。
using System;
using Guid = System.Guid;
namespace Microsoft.Sample
{
public class Program
{
}
}
但是,以下代码不会导致任何违反此规则的行为:
namespace Microsoft.Sample
{
using System;
using Guid = System.Guid;
public class Program
{
}
}
此代码将干净地编译,不会出现任何编译器错误。但是,目前尚不清楚正在分配哪个版本的 Guid 类型。如果将 using 指令移动到命名空间内部,如下所示,则会发生编译器错误:
namespace Microsoft.Sample
{
using Guid = System.Guid;
public class Guid
{
public Guid(string s)
{
}
}
public class Program
{
public static void Main(string[] args)
{
Guid g = new Guid("hello");
}
}
}
代码因以下编译器错误而失败,该错误位于包含Guid g = new Guid("hello");
CS0576: 命名空间“Microsoft.Sample”包含与别名“Guid”冲突的定义
该代码创建名为 Guid 的 System.Guid 类型的别名,并使用匹配的构造函数接口创建自己的名为 Guid 的类型。稍后,该代码将创建一个 Guid 类型的实例。若要创建此实例,编译器必须在 Guid 的两种不同定义之间进行选择。当 using-alias 指令放置在 namespace 元素之外时,编译器将选择在本地命名空间中定义的 Guid 的本地定义,并完全忽略在命名空间之外定义的 using-alias 指令。不幸的是,这在阅读代码时并不明显。
但是,当 using-alias 指令位于命名空间中时,编译器必须在两个不同的、冲突的 Guid 类型之间进行选择,这两个类型都在同一命名空间中定义。这两种类型都提供匹配的构造函数。编译器无法做出决定,因此它会标记编译器错误。
将 using-alias 指令放在命名空间之外是一种不好的做法,因为它可能会导致混淆,在这种情况下,实际使用的类型版本并不明显。这可能会导致难以诊断的错误。
将 using-alias 指令放在 namespace 元素中可以消除此错误的根源。
- 多个命名空间
在单个文件中放置多个命名空间元素通常是一个坏主意,但如果这样做,最好将所有 using 指令放在每个命名空间元素中,而不是全局放在文件顶部。这将严格限定命名空间的范围,并且还有助于避免上述行为。
需要注意的是,当使用放置在命名空间外部的 using 指令编写代码时,在命名空间内移动这些指令时应小心,以确保这不会更改代码的语义。如上所述,将 using-alias 指令放置在 namespace 元素中允许编译器在冲突类型之间进行选择,而当指令放置在命名空间之外时,不会发生这种情况。
如何解决冲突 若要解决与此规则的冲突,请将所有 using 指令和 using-alias 指令移到 namespace 元素中。
评论
using
using
当您希望使用别名时,将 using 语句放在命名空间中存在问题。别名不会从前面的语句中受益,并且必须是完全限定的。using
考虑:
namespace MyNamespace
{
using System;
using MyAlias = System.DateTime;
class MyClass
{
}
}
对:
using System;
namespace MyNamespace
{
using MyAlias = DateTime;
class MyClass
{
}
}
如果您有一个冗长的别名,例如以下内容(这就是我发现问题的方式),这可能会特别明显:
using MyAlias = Tuple<Expression<Func<DateTime, object>>, Expression<Func<TimeSpan, object>>>;
使用命名空间内的语句,它突然变成:using
using MyAlias = System.Tuple<System.Linq.Expressions.Expression<System.Func<System.DateTime, object>>, System.Linq.Expressions.Expression<System.Func<System.TimeSpan, object>>>;
不漂亮。
评论
class
using
namespace
namespace
using
这个线程已经有一些很好的答案,但我觉得我可以通过这个额外的答案带来更多细节。
首先,请记住,带有句点的命名空间声明,例如:
namespace MyCorp.TheProduct.SomeModule.Utilities
{
...
}
完全等同于:
namespace MyCorp
{
namespace TheProduct
{
namespace SomeModule
{
namespace Utilities
{
...
}
}
}
}
如果你愿意,你可以在所有这些级别上放置指令。(当然,我们只想在一个地方有 s,但根据语言,这是合法的。using
using
解析隐含类型的规则可以粗略地表述为:首先在最里面的“范围”中搜索匹配项,如果在那里找不到任何内容,则从一级到下一个范围并在那里搜索,依此类推,直到找到匹配项。如果在某个级别找到多个匹配项,如果其中一个类型来自当前程序集,则选取该类型并发出编译器警告。否则,放弃(编译时错误)。
现在,让我们在两个主要约定的具体示例中明确说明这意味着什么。
(1) 在外面使用:
using System;
using System.Collections.Generic;
using System.Linq;
//using MyCorp.TheProduct; <-- uncommenting this would change nothing
using MyCorp.TheProduct.OtherModule;
using MyCorp.TheProduct.OtherModule.Integration;
using ThirdParty;
namespace MyCorp.TheProduct.SomeModule.Utilities
{
class C
{
Ambiguous a;
}
}
在上述情况下,要找出类型是什么,搜索按以下顺序进行:Ambiguous
- 内部嵌套类型(包括继承的嵌套类型)
C
- 当前命名空间中的类型
MyCorp.TheProduct.SomeModule.Utilities
- 命名空间中的类型
MyCorp.TheProduct.SomeModule
- 类型
MyCorp.TheProduct
- 类型
MyCorp
- null 命名空间(全局命名空间)中的类型
- 、 、 、 、 和 中的类型
System
System.Collections.Generic
System.Linq
MyCorp.TheProduct.OtherModule
MyCorp.TheProduct.OtherModule.Integration
ThirdParty
另一个约定:
(2) 内含:
namespace MyCorp.TheProduct.SomeModule.Utilities
{
using System;
using System.Collections.Generic;
using System.Linq;
using MyCorp.TheProduct; // MyCorp can be left out; this using is NOT redundant
using MyCorp.TheProduct.OtherModule; // MyCorp.TheProduct can be left out
using MyCorp.TheProduct.OtherModule.Integration; // MyCorp.TheProduct can be left out
using ThirdParty;
class C
{
Ambiguous a;
}
}
现在,按以下顺序搜索类型:Ambiguous
- 内部嵌套类型(包括继承的嵌套类型)
C
- 当前命名空间中的类型
MyCorp.TheProduct.SomeModule.Utilities
- 、
System
System.Collections.Generic
System.Linq
MyCorp.TheProduct
MyCorp.TheProduct.OtherModule
MyCorp.TheProduct.OtherModule.Integration
ThirdParty
- 命名空间中的类型
MyCorp.TheProduct.SomeModule
- 类型
MyCorp
- null 命名空间(全局命名空间)中的类型
(请注意,这是“3.”的一部分,因此在“4.”和“5.”之间不需要。MyCorp.TheProduct
结束语
不管你是把 usings 放在命名空间声明里面还是外面,总有可能有人后来将具有相同名称的新类型添加到具有更高优先级的命名空间之一。
此外,如果嵌套命名空间与类型同名,则可能会导致问题。
将 usings 从一个位置移动到另一个位置总是很危险的,因为搜索层次结构会发生变化,并且可能会找到另一种类型。因此,选择一个约定并坚持下去,这样你就不必移动使用。
默认情况下,Visual Studio 的模板将 usings 放在命名空间之外(例如,如果让 VS 在新文件中生成一个新类)。
在外部使用 usings 的一个(微小的)好处是,您可以利用 using 指令来执行全局属性,例如,而不是 .[assembly: ComVisible(false)]
[assembly: System.Runtime.InteropServices.ComVisible(false)]
更新文件范围的命名空间声明
从 C# 10.0(从 2021 年开始)开始,您可以避免缩进并使用其中任一(约定 1,使用外部):
using System;
using System.Collections.Generic;
using System.Linq;
using MyCorp.TheProduct.OtherModule;
using MyCorp.TheProduct.OtherModule.Integration;
using ThirdParty;
namespace MyCorp.TheProduct.SomeModule.Utilities;
class C
{
Ambiguous a;
}
或(约定 2,内部使用):
namespace MyCorp.TheProduct.SomeModule.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using MyCorp.TheProduct;
using MyCorp.TheProduct.OtherModule;
using MyCorp.TheProduct.OtherModule.Integration;
using ThirdParty;
class C
{
Ambiguous a;
}
但同样的考虑也适用。
评论
如果那些默认使用,即源解决方案中使用的“引用”应该在命名空间之外,而那些“新添加的引用”应该放在命名空间内,那么更好的做法是你应该把它放在命名空间中。这是为了区分要添加的引用。
评论
正如 Jeppe Stig Nielsen 所说,这个线程已经有很好的答案,但我认为这个相当明显的微妙之处也值得一提。
using
在命名空间中指定的指令可以使代码更短,因为它们不需要像在外部指定时那样完全限定。
下面的示例之所以有效,是因为 和 类型都位于同一个全局命名空间 .Foo
Bar
Outer
假定代码文件 Foo.cs:
namespace Outer.Inner
{
class Foo { }
}
和 Bar.cs:
namespace Outer
{
using Outer.Inner;
class Bar
{
public Foo foo;
}
}
这可能会省略指令中的外部命名空间,简称:using
namespace Outer
{
using Inner;
class Bar
{
public Foo foo;
}
}
评论
答案中讨论了技术原因,我认为这最终取决于个人喜好,因为差异并不大,而且两者都有权衡。Visual Studio 用于创建文件的默认模板使用命名空间之外的指令,例如.cs
using
可以通过在项目文件的根目录中添加文件来调整 stylecop 以检查命名空间之外的指令,如下所示:using
stylecop.json
{
"$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
"orderingRules": {
"usingDirectivesPlacement": "outsideNamespace"
}
}
}
您可以在解决方案级别创建此配置文件,并将其作为“现有链接文件”添加到您的项目中,以便在所有项目之间共享配置。
另一个我不认为其他答案涵盖的微妙之处是,当您拥有具有相同名称的类和命名空间时。
当您在命名空间中导入时,它将找到该类。如果导入在命名空间之外,则导入将被忽略,并且类和命名空间必须是完全限定的。
//file1.cs
namespace Foo
{
class Foo
{
}
}
//file2.cs
namespace ConsoleApp3
{
using Foo;
class Program
{
static void Main(string[] args)
{
//This will allow you to use the class
Foo test = new Foo();
}
}
}
//file3.cs
using Foo; //Unused and redundant
namespace Bar
{
class Bar
{
Bar()
{
Foo.Foo test = new Foo.Foo();
Foo test = new Foo(); //will give you an error that a namespace is being used like a class.
}
}
}
评论
file3.cs
Foo.Foo
using
file2.cs
Foo.DeeperNamespace.SomeClass
Main
Foo
Foo
global::Foo.DeeperNamespace
我遇到的一个皱纹(其他答案中没有涵盖):
假设您有以下命名空间:
- 某物.其他
- Parent.Something.其他
当您在 之外使用 时,它指的是第一个 (Something.Other)。using Something.Other
namespace Parent
但是,如果在该命名空间声明中使用它,它将引用第二个命名空间声明 (Parent.Something.Other)!
有一个简单的解决方案:添加 “” 前缀:docsglobal::
namespace Parent
{
using global::Something.Other;
// etc
}
评论
通常,外部指令(例如系统和 Microsoft 命名空间)应放置在指令外部。除非另有说明,否则它们是应在所有情况下应用的默认值。这应包括不属于当前项目的任何组织内部库,或引用同一项目中其他主命名空间的指令。引用当前项目和命名空间中其他模块的任何指令都应放在指令内。这有两个特定功能:using
namespace
using
using
namespace
- 它提供了本地模块和“其他”模块(即其他所有模块)之间的视觉区分。
- 它确定要优先应用的本地指令的范围,而不是全局指令。
后一个原因很重要。这意味着很难引入一个模棱两可的引用问题,而该问题可以通过不比重构代码更重要的更改来引入。也就是说,您将方法从一个文件移动到另一个文件,突然出现了一个以前不存在的错误。通俗地说,这是一种“heisenbug”——历史上很难追踪。
评论
尚未提及:
将指令放在命名空间声明中是众所周知的最佳编程实践的应用,即在尽可能小的范围内声明所有内容。using
如果最佳编程实践是你的第二天性,那么你会自动做这样的事情。
这可能是将 using 指令放在命名空间声明中的最佳理由,而不管其他地方提到的(边缘)技术(边缘)优点如何;就这么简单。
已经提到过,但也许更好地说明:
将指令放在命名空间中可以避免不必要的重复,并使我们的声明更加简洁。using
这太简洁了:
using Com.Acme.Products.Traps.RoadRunnerTraps;
namespace Com.Acme.Products.Traps {
这很甜蜜,切中要害:
namespace Com.Acme.Products.Traps {
using RoadRunnerTraps;
虽然大多数答案倾向于将指令放在声明中,但有趣的是,Microsoft 在 C# 编码约定中的建议另有说明:using
namespace
将 using 指令放在命名空间声明之外
当 using 指令位于命名空间声明之外时, 导入的命名空间是其完全限定的名称。这更清楚了。 当 using 指令位于命名空间内时,它可以是 相对于该命名空间或其完全限定名称。那是 模糊。
然而,在另一篇文章中,他们规定了相反的规定:
using 语句应位于命名空间声明中。
这要视情况而定。 根据 StyleCop 的说法,里面。 据Microsoft说,外面。
(只是想要一个真正的简短答案,我阅读了整个线程,但不是每个人都有时间)
上一个:一次捕获多个异常?
下一个:如何在 C# 中生成随机整数?
评论
using
using
using
using (var e = s.GetEnumerator()) { /* ... */ }
var e = s.GetEnumerator(); try { /* ... */ } finally { if (e != null) { e.Dispose(); } }
using
namespace