" using"指令应该在名称空间之内还是之外?

时间:2020-03-06 14:38:29  来源:igfitidea点击:

我已经在某些Ccode上运行了StyleCop,并且一直在报告我的using指令应位于名称空间内。

将" using"指令放在名称空间内部而不是名称空间外部是有技术原因的吗?

解决方案

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

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使用指令和程序集加载...以及其他此类文章,技术上没有区别。

我的偏好是将它们放在名称空间之外。

两者之间实际上存在(细微)差异。假设我们在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.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中会发生什么。在这种情况下,无论File中的" using"位置在哪里,在File2中添加Outer.Math都会破坏File1. 这意味着编译器在查看任何" using"指令之前先搜索最内层的命名空间。

根据StyleCop文档:

SA1200:在名称空间中使用DirectivesMustBePlacedWith

原因
Cusing指令放置在名称空间元素的外部。

规则说明
当将using指令或者using-alias指令放置在名称空间元素之外时,会发生违反此规则的情况,除非文件不包含任何名称空间元素。

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

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指令放置在namespace元素之外时,编译器将选择在本地名称空间内定义的Guid的本地定义,并完全忽略在namespace之外定义的using-alias指令。不幸的是,在阅读代码时,这并不明显。

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

将using-alias指令放置在名称空间之外是一种不好的做法,因为在这种情况下(实际上不知道实际使用的是哪个版本),这可能导致混乱。这可能会导致可能难以诊断的错误。

将using-alias指令放置在namespace元素中可消除这种情况,使之成为错误的来源。

  • 多个命名空间

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

重要的是要注意,当使用放置在名称空间之外的指令编写代码时,在将这些指令移至名称空间内时应格外小心,以确保这不会改变代码的语义。如上所述,将using-alias指令放置在namespace元素中,可使编译器以冲突的方式进行选择,方式是将指令放置在命名空间之外时不会发生。

如何解决违规
要解决违反此规则的问题,请在命名空间元素内移动所有using指令和using-alias指令。