我一直在一些C#代码上运行StyleCop,它不断报告我的using指令应该在命名空间中。

是否有技术原因将using指令放在命名空间内部而不是外部?


当前回答

根据StyleCop文档:

SA1200:使用方向必须放置在命名空间中

原因C#using指令位于命名空间元素的外部。

规则说明如果将using指令或using别名指令放置在命名空间元素之外,则会违反此规则,除非文件不包含任何命名空间元素。

例如,以下代码将导致两次违反此规则。

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”冲突的定义

该代码为System.Guid类型创建一个名为Guid的别名,并使用匹配的构造函数接口创建自己的类型Guid。稍后,代码将创建Guid类型的实例。要创建此实例,编译器必须在Guid的两个不同定义之间进行选择。当using alias指令置于命名空间元素之外时,编译器将选择在本地命名空间中定义的Guid的本地定义,并完全忽略在命名空间之外定义的using alis指令。不幸的是,这在阅读代码时并不明显。

但是,当using alias指令位于命名空间中时,编译器必须在同一命名空间中定义的两种不同的、冲突的Guid类型之间进行选择。这两种类型都提供了匹配的构造函数。编译器无法做出决定,因此会标记编译器错误。

将using alias指令放在命名空间之外是一种不好的做法,因为在这样的情况下,这可能会导致混淆,因为不清楚实际使用的是哪种类型的版本。这可能会导致难以诊断的错误。

在命名空间元素中放置使用别名指令可以消除这一错误源。

多个命名空间

在一个文件中放置多个名称空间元素通常是一个坏主意,但如果这样做了,最好将所有using指令放置在每个名称空间元素中,而不是全局放置在文件的顶部。这将严格限定命名空间的范围,也将有助于避免上述行为。

需要注意的是,当使用放置在命名空间外部的指令编写代码时,在命名空间内移动这些指令时应小心,以确保这不会改变代码的语义。如上所述,在命名空间元素中放置使用别名指令允许编译器在冲突类型之间进行选择,而当指令放置在命名空间之外时,这种选择不会发生。

如何修复违规要修复违反此规则的问题,请在命名空间元素中移动所有using指令和using别名指令。

其他回答

正如杰佩·斯蒂格·尼尔森(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;
    }
}

这可能会省略using指令中的外部命名空间,简称:

namespace Outer
{
    using Inner;

    class Bar
    {
        public Foo foo;
    }
}

如果源解决方案中使用的默认引用(即“引用”)应该在命名空间之外,而那些“新添加的引用”是一个很好的做法,则应将其放在命名空间内。这是为了区分要添加的引用。

答案中讨论了技术原因,我认为这最终取决于个人偏好,因为差异没有那么大,而且两者都存在权衡。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"
    }
  }
}

您可以在解决方案级别创建此配置文件,并将其作为“现有链接文件”添加到项目中,以便在所有项目中共享配置。

将其放在名称空间中会使声明成为文件的该名称空间的本地声明(如果文件中有多个名称空间),但如果每个文件只有一个名称空间,那么它们在名称空间外部还是内部都没有太大区别。

using ThisNamespace.IsImported.InAllNamespaces.Here;

namespace Namespace1
{ 
   using ThisNamespace.IsImported.InNamespace1.AndNamespace2;

   namespace Namespace2
   { 
      using ThisNamespace.IsImported.InJustNamespace2;
   }       
}

namespace Namespace3
{ 
   using ThisNamespace.IsImported.InJustNamespace3;
}

我遇到了一条皱纹(其他答案中没有提到):

假设您有以下名称空间:

有些东西。其他父级.某些.其他

当您在命名空间Parent之外使用Something.Other时,它指的是第一个(Something.Other)。

但是,如果在该命名空间声明中使用它,它将引用第二个(Parent.Something.Other)!

有一个简单的解决方案:添加“global::”前缀:docs

namespace Parent
{
   using global::Something.Other;
   // etc
}