'using'指令应该在命名空间的内部还是外部?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了'using'指令应该在命名空间的内部还是外部?相关的知识,希望对你有一定的参考价值。
我一直在运行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
指令之前搜索using
,因此它找到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;
}
}
}
现在编译器在搜索System
之前搜索Outer
,找到System.Math
,一切都很好。
有些人认为Math
可能是用户定义类的坏名称,因为System
中已有一个;这里的重点只是存在差异,它会影响代码的可维护性。
值得注意的是,如果Foo
位于名称空间Outer
而不是Outer.Inner
,会发生什么。在这种情况下,无论Outer.Math
在哪里,在File2中添加using
都会破坏File1。这意味着编译器在查看任何using
指令之前搜索最里面的封闭命名空间。
在答案中讨论了技术原因,我认为最终涉及个人偏好,因为差异并不大,而且两者都存在权衡。 Visual Studio的用于创建//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.
}
}
}
文件的默认模板使用命名空间之外的using Something.Other
指令,例如
通过在项目文件的根目录中添加namespace Parent
文件,可以调整stylecop以检查命名空间之外的global::
指令:
docs
您可以在解决方案级别创建此配置文件,并将其作为“现有链接文件”添加到项目中,以便在所有项目中共享配置。
如果那些默认使用,即源解决方案中使用的“引用”应该在命名空间之外,而那些是“新添加的引用”是一个好的做法,那么你应该将它放在命名空间中。这是为了区分添加的引用。
这个帖子已经有了一些很好的答案,但我觉得我可以通过这个额外的答案带来更多细节。
首先,请记住带有句点的名称空间声明,例如:
namespace MyCorp.TheProduct.SomeModule.Utilities
{
...
}
完全等同于:
namespace MyCorp
{
namespace TheProduct
{
namespace SomeModule
{
namespace Utilities
{
...
}
}
}
}
如果你愿意,你可以将using
指令放在所有这些级别上。 (当然,我们希望只在一个地方使用using
s,但根据语言它是合法的。)
解析哪种类型的规则可以这样松散地说:首先搜索最内部的“范围”以进行匹配,如果没有找到任何内容,那么将一个级别输出到下一个范围并在那里搜索,依此类推,直到找到匹配项。如果在某个级别找到多个匹配项,如果其中一个类型来自当前程序集,则选择该类型并发出编译器警告。否则,放弃(编译时错误)。
现在,让我们在两个主要约定的具体例子中明确这意味着什么。
(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命名空间中的类型(全局命名空间)
- 在qazxsw poi,qazxsw poi,qazxsw poi,
System
,System.Collections.Generic
和System.Linq
中的类型
另一个惯例:
(2)内部使用:
MyCorp.TheProduct.OtherModule
现在,按以下顺序搜索MyCorp.TheProduct.OtherModule.Integration
类型:
ThirdParty
中的嵌套类型(包括继承的嵌套类型)- 当前名称空间
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; } }
中的类型 - 在qazxsw poi,qazxsw poi,qazxsw poi,
Ambiguous
,C
,MyCorp.TheProduct.SomeModule.Utilities
和System
中的类型 - 命名空间
System.Collections.Generic
中的类型 System.Linq
中的类型- null命名空间中的类型(全局命名空间)
(请注意,MyCorp.TheProduct
是“3.”的一部分,因此在“4.”和“5.”之间不需要。)
结束语
无论你是将命令放在命名空间声明的内部还是外部,总有可能以后有人将具有相同名称的新类型添加到具有更高优先级的命名空间之一。
此外,如果嵌套命名空间与类型具有相同的名称,则可能会导致问题。
将使用从一个位置移动到另一个位置总是很危险的,因为搜索层次结构发生了变化,可能会找到另一种类型。因此,选择一个约定并坚持下去,这样你就不必再使用它了。
默认情况下,Visual Studio模板将使用放在命名空间之外(例如,如果您使VS在新文件中生成新类)。
在外部使用的一个(微小的)优点是,您可以将using指令用于全局属性,例如MyCorp.TheProduct.OtherModule
而不是MyCorp.TheProduct.OtherModule.Integration
。
将它放在命名空间内会使文件的该命名空间的声明是本地的(如果文件中有多个命名空间),但是如果每个文件只有一个命名空间,那么它们是否在外面或者它们没有多大区别在命名空间内。
ThirdParty
根据MyCorp.TheProduct.SomeModule
和其他此类文章,技术上没有区别。
我的偏好是将它们放在命名空间之外。
根据StyleCop文档:
SA1200:UsingDirectivesMustBePlacedWithinNamespace
原因C#using指令放在名称空间元素之外。
规则说明如果将using指令或using-alias指令放在namespace元素之外,则会违反此规则,除非该文件不包含任何名称空间元素。
例如,以下代码将导致两次违反此规则。
MyCorp
但是,以下代码不会导致违反此规则:
MyCorp.TheProduct
此代码将干净地编译,没有任何编译器错误。但是,尚不清楚正在分配哪种版本的Guid类型。如果在命名空间内移动using指令,如下所示,将发生编译器错误:
[assembly: ComVisible(false)]
代码在包含[assembly: System.Runtime.InteropServices.ComVisible(false)]
的行上找到的以下编译器错误失败
CS0576:命名空间'Microsoft.Sample'包含与别名'Guid'冲突的定义
该代码为名为Guid的System.Guid类型创建了一个别名,并且还创建了自己的类型,称为Guid,具有匹配的构造函数接口。稍后,代码将创建Guid类型的实例。要创建此实例,编译器必须在Guid的两个不同定义之间进行选择。当using-alias指令放在namespace元素之外时,编译器将选择在本地名称空间中定义的Guid的本地定义,并完全忽略在名称空间外定义的using-alias指令。不幸的是,这在阅读代码时并不明显。
但是,当using-alias指令位于命名空间内时,编译器必须在同一命名空间中定义的两种不同的,冲突的Guid类型之间进行选择。这两种类型都提供了匹配的构造函数。编译器无法做出决定,因此它会标记编译器错误。
将using-alias指令放在命名空间之外是一种不好的做法,因为它可能会导致这种情况混淆,在这种情况下,实际使用的是哪种类型的版本并不明显。这可能会导致可能难以诊断的错误。
在namespace元素中放置using-alias指令会将其作为bug的来源消除。
- 多个命名空间
在单个文件中放置多个名称空间元素通常是一个坏主意,但是如果这样做,最好将所有using指令放在每个名称空间元素中,而不是全局放在文件的顶部。这将严格限定命名空间的范围,并且还有助于避免上述类型的行为。
重要的是要注意,当使用位于命名空间之外的using指令编写代码时,在命名空间中移动这些指令时应小心,以确保这不会改变代码的语义。如上所述,在namespace元素中放置using-alias指令允许编译器以指令放置在命名空间之外时不会发生的方式在冲突类型之间进行选择。
如何修复违规要修复违反此规则的行为,请在namespace元素中移动所有using指令和using-alias指令。
当您希望使用别名时,在命名空间内放置使用语句会出现问题。别名不会受益于早期的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 - Using Directive and Assembly Loading...以上是关于'using'指令应该在命名空间的内部还是外部?的主要内容,如果未能解决你的问题,请参考以下文章
C# 未能找到类型或命名空间名称“Form1”(是不是缺少 using 指令或程序集引用?)
当在C#中使用System指令时,找不到类型或名称空间名称'Text'
我们啥时候需要将 using 指令放在命名空间范围内? [复制]
错误 1 未能找到类型或命名空间名称“Form1”(是不是缺少 using 指令或程序集引用?)