ben*_*rce 1975 .net c# namespaces stylecop code-organization
我一直在运行StyleCop而不是一些C#代码,并且它一直报告我的using指令应该在命名空间内.
是否存在将using指令放入命名空间而不是命名空间外的技术原因?
Cha*_*lie 2050
两者之间实际上存在(微妙的)差异.想象一下,你在File1.cs中有以下代码:
// File1.cs
using System;
namespace Outer.Inner
{
class Foo
{
static void Bar()
{
double d = Math.PI;
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在想象有人将另一个文件(File2.cs)添加到项目中,如下所示:
// File2.cs
namespace Outer
{
class Math
{
}
}
Run Code Online (Sandbox Code Playgroud)
编译器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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在编译器搜索 System在搜索Outer,查找之前搜索,System.Math一切都很好.
有些人认为,Math对于用户定义的类可能是一个坏名称,因为已经有一个System ; 这里要说的就是这样存在的差异,并且它会影响您的代码的可维护性.
注意如果Foo在命名空间中Outer,而不是在命名空间中会发生什么,这也很有趣Outer.Inner.在这种情况下,Outer.Math无论在哪里,添加File2都会破坏File1using.这意味着编译器在查看任何using指令之前搜索最里面的封闭命名空间.
Jep*_*sen 430
这个帖子已经有了一些很好的答案,但我觉得我可以通过这个额外的答案带来更多细节.
首先,请记住带有句点的名称空间声明,例如:
namespace MyCorp.TheProduct.SomeModule.Utilities
{
...
}
Run Code Online (Sandbox Code Playgroud)
完全等同于:
namespace MyCorp
{
namespace TheProduct
{
namespace SomeModule
{
namespace Utilities
{
...
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果你愿意,你可以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;
}
}
Run Code Online (Sandbox Code Playgroud)
在上面的例子中,为了找出什么类型Ambiguous,搜索顺序如下:
C(包括继承的嵌套类型)MyCorp.TheProduct.SomeModule.UtilitiesMyCorp.TheProduct.SomeModuleMyCorp.TheProductMyCorpSystem,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;
}
}
Run Code Online (Sandbox Code Playgroud)
现在,Ambiguous按以下顺序搜索类型:
C(包括继承的嵌套类型)MyCorp.TheProduct.SomeModule.UtilitiesSystem,System.Collections.Generic,System.Linq,MyCorp.TheProduct,MyCorp.TheProduct.OtherModule,MyCorp.TheProduct.OtherModule.Integration,和ThirdPartyMyCorp.TheProduct.SomeModuleMyCorp(注意,它MyCorp.TheProduct是"3."的一部分,因此在"4."和"5."之间不需要.)
结束语
无论你是将命令放在命名空间声明的内部还是外部,总有可能以后有人将具有相同名称的新类型添加到具有更高优先级的命名空间之一.
此外,如果嵌套命名空间与类型具有相同的名称,则可能会导致问题.
将使用从一个位置移动到另一个位置总是很危险的,因为搜索层次结构发生了变化,可能会找到另一种类型.因此,选择一个约定并坚持下去,这样你就不必再使用它了.
默认情况下,Visual Studio的模板将使用放在命名空间之外(例如,如果您使VS在新文件中生成新类).
在外部使用的一个(微小的)优点是,您可以将using指令用于全局属性,例如[assembly: ComVisible(false)]代替[assembly: System.Runtime.InteropServices.ComVisible(false)].
Mar*_*ade 190
将它放在命名空间中会使文件的该命名空间的声明是本地的(如果文件中有多个命名空间),但是如果每个文件只有一个命名空间,那么它们是否外出或者它们没有多大区别在命名空间内.
using ThisNamespace.IsImported.InAllNamespaces.Here;
namespace Namespace1
{
using ThisNamespace.IsImported.InNamespace1.AndNamespace2;
namespace Namespace2
{
using ThisNamespace.IsImported.InJustNamespace2;
}
}
namespace Namespace3
{
using ThisNamespace.IsImported.InJustNamespace3;
}
Run Code Online (Sandbox Code Playgroud)
Qui*_*son 61
根据Hanselman - 使用指令和装配......以及其他此类文章,技术上没有区别.
我的偏好是将它们放在命名空间之外.
Jar*_*rak 48
根据StyleCop文档:
SA1200:UsingDirectivesMustBePlacedWithinNamespace
原因AC#using指令放在命名空间元素之外.
规则说明如果将using指令或using-alias指令放在namespace元素之外,则会违反此规则,除非该文件不包含任何名称空间元素.
例如,以下代码将导致两次违反此规则.
using System;
using Guid = System.Guid;
namespace Microsoft.Sample
{
public class Program
{
}
}
Run Code Online (Sandbox Code Playgroud)
但是,以下代码不会导致违反此规则:
namespace Microsoft.Sample
{
using System;
using Guid = System.Guid;
public class Program
{
}
}
Run Code Online (Sandbox Code Playgroud)
此代码将干净地编译,没有任何编译器错误.但是,尚不清楚正在分配哪种版本的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");
}
}
}
Run Code Online (Sandbox Code Playgroud)
代码在包含的行上找到的以下编译器错误失败 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指令放在命名空间之外是一种不好的做法,因为它可能会导致这种情况混淆,在这种情况下,实际使用的是哪种类型的版本并不明显.这可能会导致可能难以诊断的错误.
在namespace元素中放置using-alias指令会将其作为bug的来源消除.
在单个文件中放置多个名称空间元素通常是一个坏主意,但是如果这样做,最好将所有using指令放在每个名称空间元素中,而不是全局放在文件的顶部.这将严格限定命名空间的范围,并且还有助于避免上述类型的行为.
重要的是要注意,当使用位于命名空间之外的using指令编写代码时,在命名空间中移动这些指令时应小心,以确保这不会改变代码的语义.如上所述,在namespace元素中放置using-alias指令允许编译器以指令放置在命名空间之外时不会发生的方式在冲突类型之间进行选择.
如何修复违规要修复违反此规则的行为,请在namespace元素中移动所有using指令和using-alias指令.
Neo*_*Neo 34
当您希望使用别名时,在命名空间内放置使用语句会出现问题.别名不会从早期的using陈述中受益,必须完全合格.
考虑:
namespace MyNamespace
{
using System;
using MyAlias = System.DateTime;
class MyClass
{
}
}
Run Code Online (Sandbox Code Playgroud)
与:
using System;
namespace MyNamespace
{
using MyAlias = DateTime;
class MyClass
{
}
}
Run Code Online (Sandbox Code Playgroud)
如果您有一个冗长的别名,例如以下(这是我发现问题的方式),这可能会特别明显:
using MyAlias = Tuple<Expression<Func<DateTime, object>>, Expression<Func<TimeSpan, object>>>;
Run Code Online (Sandbox Code Playgroud)
使用using命名空间内的语句,它突然变为:
using MyAlias = System.Tuple<System.Linq.Expressions.Expression<System.Func<System.DateTime, object>>, System.Linq.Expressions.Expression<System.Func<System.TimeSpan, object>>>;
Run Code Online (Sandbox Code Playgroud)
不漂亮.
我遇到的一个皱纹(其他答案中没有提到):
假设您有这些命名空间:
当您using Something.Other 在a之外使用时namespace Parent,它指的是第一个 (Something.Other)。
但是,如果您在该名称空间声明中使用它,则它指的是第二个 (Parent.Something.Other)!
有一个简单的解决方案:添加“ global::”前缀:docs
namespace Parent
{
using global::Something.Other;
// etc
}
Run Code Online (Sandbox Code Playgroud)
正如 Jeppe Stig Nielsen所说,这个帖子已经有了很好的答案,但我认为这个相当明显的微妙之处也值得一提。
using 在命名空间内指定的指令可以使代码更短,因为它们不需要像在外部指定时那样完全限定。
以下示例有效,因为类型Foo和Bar都在同一个全局命名空间Outer.
假设代码文件Foo.cs:
namespace Outer.Inner
{
class Foo { }
}
Run Code Online (Sandbox Code Playgroud)
和Bar.cs:
namespace Outer
{
using Outer.Inner;
class Bar
{
public Foo foo;
}
}
Run Code Online (Sandbox Code Playgroud)
这可能会省略using指令中的外部命名空间,简称:
namespace Outer
{
using Inner;
class Bar
{
public Foo foo;
}
}
Run Code Online (Sandbox Code Playgroud)
答案中讨论了技术原因,我认为最终涉及个人喜好,因为差异不是很大,而且两者都需要权衡。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"
}
}
}
Run Code Online (Sandbox Code Playgroud)
您可以在解决方案级别创建此配置文件,并将其作为“现有链接文件”添加到您的项目中,以便在所有项目之间共享配置。
using将指令放置在名称空间声明中是众所周知的最佳编程实践的应用,即在尽可能小的范围内声明所有内容。
如果最佳编程实践是您的第二天性,那么您会自动执行类似的操作。
这可能是将您的 using 指令放入名称空间声明中的最佳理由,无论其他地方提到的(边界)技术(边界)优点如何;就这么简单。
using将指令放置在命名空间内可以避免不必要的重复,并使我们的声明更加简洁。
这是不必要的简洁:
using Com.Acme.Products.Traps.RoadRunnerTraps;
namespace Com.Acme.Products.Traps {
Run Code Online (Sandbox Code Playgroud)
这很甜蜜,也很切中要点:
namespace Com.Acme.Products.Traps {
using RoadRunnerTraps;
Run Code Online (Sandbox Code Playgroud)
我认为其他答案没有涵盖的另一个微妙之处是当您有一个同名的类和命名空间时。
当您在命名空间中导入时,它将找到该类。如果导入在命名空间之外,那么导入将被忽略并且类和命名空间必须是完全限定的。
//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.
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
193750 次 |
| 最近记录: |