我一直在一些C#代码上运行StyleCop,它不断报告我的using指令应该在命名空间中。
是否有技术原因将using指令放在命名空间内部而不是外部?
我一直在一些C#代码上运行StyleCop,它不断报告我的using指令应该在命名空间中。
是否有技术原因将using指令放在命名空间内部而不是外部?
当前回答
我遇到了一条皱纹(其他答案中没有提到):
假设您有以下名称空间:
有些东西。其他父级.某些.其他
当您在命名空间Parent之外使用Something.Other时,它指的是第一个(Something.Other)。
但是,如果在该命名空间声明中使用它,它将引用第二个(Parent.Something.Other)!
有一个简单的解决方案:添加“global::”前缀:docs
namespace Parent
{
using global::Something.Other;
// etc
}
其他回答
答案中讨论了技术原因,我认为这最终取决于个人偏好,因为差异没有那么大,而且两者都存在权衡。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"
}
}
}
您可以在解决方案级别创建此配置文件,并将其作为“现有链接文件”添加到项目中,以便在所有项目中共享配置。
事实上,两者之间有(微妙的)区别。假设您在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;
}
}
}
现在编译器先搜索System,再搜索Outer,找到System.Math,一切正常。
有些人会认为Math对于用户定义的类来说可能是个坏名字,因为System中已经有一个;这里的重点是存在差异,这会影响代码的可维护性。
如果Foo位于命名空间Outer,而不是Outer.Inner,会发生什么也很有趣。在这种情况下,在File2中添加Outer.Math会破坏File1,而不管使用的位置如何。这意味着编译器在查看任何using指令之前搜索最内部的封闭命名空间。
尚未提及:
将using指令放在命名空间声明中是众所周知的最佳编程实践的一个应用,即在尽可能小的范围内声明所有内容。
如果最佳编程实践是您的第二天性,那么您可以自动执行类似的操作。
这可能是将using指令放在命名空间声明中的最佳理由,而不管其他地方提到的(边界)技术(边界)优点;就这么简单。
已经提到了,但可能更好地说明了:
在名称空间中放置using指令可以避免不必要的重复,并使声明更加简洁。
这是不必要的简洁:
using Com.Acme.Products.Traps.RoadRunnerTraps;
namespace Com.Acme.Products.Traps {
这是甜蜜的,切中要害:
namespace Com.Acme.Products.Traps {
using RoadRunnerTraps;
当您希望使用别名时,在名称空间中放置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 MyAlias = System.Tuple<System.Linq.Expressions.Expression<System.Func<System.DateTime, object>>, System.Linq.Expressions.Expression<System.Func<System.TimeSpan, object>>>;
不漂亮。
另一个我认为没有被其他答案涵盖的微妙之处是,当你有一个同名的类和命名空间时。
当您在名称空间中导入时,它将找到该类。如果导入在命名空间之外,那么将忽略导入,并且类和命名空间必须完全限定。
//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.
}
}
}