应该使用'的指示是内部或外部的空间?
-
02-07-2019 - |
题
我已经运行 StyleCop 在某些C#代码,它使报告的,我的 using
指示应该是内部的名称空间。
有没有技术理由把 using
指令的内部而不是外面的空间?
解决方案
两者之间实际上存在(微妙的)差异。想象一下,你在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
{
}
}
编译器在使用命名空间外的指令查找之前搜索
Outer
,因此找到 Outer.Math
而不是 System .Math 代码>。不幸的是(或者幸运的是?),
Outer.Math
没有 PI
成员,所以File1现在已经坏了。
如果将 using
放在名称空间声明中,则会发生这种情况,如下所示:
// File1b.cs
namespace Outer.Inner
{
using System;
class Foo
{
static void Bar()
{
double d = Math.PI;
}
}
}
现在编译器在搜索 Outer
之前搜索 System
,找到 System.Math
,一切都很好。
有些人认为 Math
可能是用户定义类的错误名称,因为 System
中已有一个;这里的重点是 是一个区别,它会影响代码的可维护性。
如果 Foo
在命名空间 Outer
中,而不是 Outer.Inner
,会发生什么事情也很有意思。在这种情况下,在File2中添加 Outer.Math
会破坏File1,无论使用
的位置如何。这意味着编译器在使用指令查看任何之前搜索最里面的封闭命名空间。
其他提示
这个帖子已经有了一些很好的答案,但我觉得我可以通过这个额外的答案带来更多细节。
首先,请记住带有句点的名称空间声明,例如:
namespace MyCorp.TheProduct.SomeModule.Utilities
{
...
}
完全等同于:
namespace MyCorp
{
namespace TheProduct
{
namespace SomeModule
{
namespace Utilities
{
...
}
}
}
}
如果您愿意,可以在所有这些级别上使用指令放置。 (当然,我们希望只在一个地方使用
使用
,但根据语言它是合法的。)
解决哪种类型的隐含规则可以像这样松散地说明:首先搜索最内部的“范围”。对于一场比赛,如果没有找到任何内容,那么在下一个范围出现一个级别并在那里搜索,依此类推,直到找到匹配为止。如果在某个级别找到多个匹配项,如果其中一个类型来自当前程序集,则选择该类型并发出编译器警告。否则,放弃(编译时错误)。
现在,让我们明确说明这在两个主要惯例的具体例子中意味着什么。
(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
醇>
-
C
(包括继承的嵌套类型) 中的嵌套类型
- 当前命名空间中的类型
MyCorp.TheProduct.SomeModule.Utilities
-
System
,System.Collections.Generic
,System.Linq
,MyCorp.TheProduct
,< code> MyCorp.TheProduct.OtherModule ,MyCorp.TheProduct.OtherModule.Integration
和ThirdParty
- 命名空间
MyCorp.TheProduct.SomeModule
中的类型
-
MyCorp
中的类型
- null 命名空间(全局命名空间)中的类型 醇>
另一个惯例:
(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
类型:
(注意 MyCorp.TheProduct
是“3.”的一部分,因此在“4.”和“5.”之间不需要。)
结束语
无论你是否在命名空间声明的内部或外部放置了使用,总有可能以后有人将具有相同名称的新类型添加到具有更高优先级的命名空间之一。
此外,如果嵌套命名空间与类型具有相同的名称,则可能会导致问题。
将使用从一个位置移动到另一个位置总是很危险的,因为搜索层次结构会发生变化,并且可能会找到另一种类型。因此,选择一个约定并坚持下去,这样你就不必再使用它了。
默认情况下,Visual Studio的模板将命名空间的放在之外(例如,如果你让VS在新文件中生成一个新类)。
使用外部的一个(微小的)优点是,您可以将using指令用于全局属性,例如 [assembly:ComVisible(false)]
而不是 [assembly:System.Runtime.InteropServices.ComVisible(false)]
。
将它放在命名空间中会使该文件的该命名空间的声明是本地的(如果文件中有多个命名空间),但是如果每个文件只有一个命名空间,那么它们是否有很大区别走到命名空间的外面或里面。
using ThisNamespace.IsImported.InAllNamespaces.Here;
namespace Namespace1
{
using ThisNamespace.IsImported.InNamespace1.AndNamespace2;
namespace Namespace2
{
using ThisNamespace.IsImported.InJustNamespace2;
}
}
namespace Namespace3
{
using ThisNamespace.IsImported.InJustNamespace3;
}
根据 Hanselman - 使用指令和程序集加载...... 和其他此类文章技术上没有区别。
我的偏好是将它们放在命名空间之外。
根据StyleCop文件:
SA1200:UsingDirectivesMustBePlacedWithinnamespace
因 C#使用指令置之外的一个名字空间元。
规则说明 违反这一规则时出现一个使用指令或使用别名指令置之外的一个名字空间元,除非该文件不包含任何名字空间的要素。
例如,以下代码将导致在两个违反这一规则。
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类型是正在进行分配。如果使用指令的内部移动空间,如下所示,编译器错误将会发生:
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:Namespace'Microsoft。样品中包含的定义相互矛盾的与alias'Guid'
代码创建一个别系统。Guid类型称为Guid,并且还创造了它自己的类型称为Guid有匹配的构造的接口。后来,代码创建的一个实例类型Guid。创建这个实例,编译器必须选择之间两种不同的定义Guid。当使用别名指令被放置在外空间元,将编译器选择的地方定义的Guid定义在当地的名字空间,并完全忽略使用别名指令的定义以外的名称空间。这个,不幸的是,并不是显而易见的当阅读了代码。
当使用别名指令》内的定位的名字空间,但是,已编译器之间作出选择的两个不同的、相互冲突的Guid类型的两个定义相同的名称空间。这两种类型提供匹配的构造。编译是无法做出决定,因此,它标志的编译错误。
将使用别名令以外的名称空间是一个糟糕的做法,因为它可能导致混乱,在这样的情况下,这不是显而易见的哪个版本的类型实际上是被使用。这可能导致错误,这可能是困难的诊断。
将使用别名指令的名称空间内的单元中消除了这个源的的错误。
- 多个名字空间
把多个元素名称空间内的一个单一的文件通常是一个糟糕的想法,但如果以及当这样做时,它是一个很好的想法的地方所使用的指令内的各个空间的元素,而不是全球顶部的文件。这将范围的名字空间的紧密,并且还将有助于避免这种行为的描述上。
重要的是要注意的是,当代码已被写入与使用的指令置于外空间时,应当注意时移动这些指令的名称空间内,以确保这不会改变的语义的代码。如上面所解释的,将使用别名指令内的空间元允许编译器选择之间的冲突类型的方式,将不会发生当的指示被置之外的名称空间。
如何解决侵犯 解决违反了这项规则,将所有使用指令和使用别名指令内的空间元。
当您希望使用别名时,在命名空间中放置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 MyAlias = System.Tuple<System.Linq.Expressions.Expression<System.Func<System.DateTime, object>>, System.Linq.Expressions.Expression<System.Func<System.TimeSpan, object>>>;
不漂亮。
正如Jeppe Stig Nielsen 所说,这个帖子已经有了很好的答案,但我认为这个相当明显的微妙之处值得一提太
使用命名空间内指定的
指令可以缩短代码,因为它们不需要完全限定,就像在外部指定它们一样。
以下示例有效,因为 Foo
和 Bar
类型都在同一个全局命名空间中, Outer
。
设定代码文件 Foo.cs :
namespace Outer.Inner
{
class Foo { }
}
Bar.cs :
namespace Outer
{
using Outer.Inner;
class Bar
{
public Foo foo;
}
}
这可能会在中使用
指令省略外部命名空间,简而言之:
namespace Outer
{
using Inner;
class Bar
{
public Foo foo;
}
}
一个皱纹,我遇到了(不涵盖的其他答复):
假设你有这些名称空间:
- 什么东西。其他的
- 父母。什么东西。其他的
当你使用 using Something.Other
外面 的 namespace Parent
, 它指的是第一人(什么东西。其他)。
但是如果你用它 内部 这名字空间的宣言,它指的是第二人(父母。什么东西。其他)!
有一个简单的解决办法:添加"global::
"前缀: 文档
namespace Parent
{
using global::Something.Other;
// etc
}
我不相信的另一个微妙之处是其他答案所涵盖的是当你有一个具有相同名称的类和命名空间时。
当您在命名空间内导入时,它将找到该类。如果导入位于命名空间之外,则导入将被忽略,类和命名空间必须完全限定。
//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();
}
}
}
//file2.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.
}
}
}
技术原因在答案中讨论,我认为最终涉及个人偏好,因为差异不是 big ,而且两者都存在权衡。 Visual Studio的用于创建 .cs
文件的默认模板使用在命名空间之外使用
指令,例如。
可以通过在项目文件的根目录中添加 stylecop.json
文件来调整stylecop以使用指令在命名空间之外检查,其中包含以下内容:
{
"$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
"orderingRules": {
"usingDirectivesPlacement": "outsideNamespace"
}
}
}
您可以在解决方案级别创建此配置文件,并将其作为“现有链接文件”添加到项目中,以便在所有项目中共享配置。
如果那些默认使用ie <“em> references ”,那么这是一种更好的做法。在源代码解决方案中使用的应该在命名空间之外,而那些“new added reference”是一个很好的做法,你应该将它放在命名空间中。这是为了区分正在添加的引用。