协慌网

登录 贡献 社区

“使用” 指令应该在命名空间内部还是外部?

我一直在运行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
    {
    }
}

编译器在查看using命名空间之外的指令之前搜索Outer ,因此它找到Outer.Math而不是System.Math 。不幸的是(或者幸运的是?), Outer.Math没有PI成员,所以Outer.Math现在已经坏了。

如果将using放在命名空间声明中,则会发生这种情况,如下所示:

// File1b.cs
namespace Outer.Inner
{
    using System;
    class Foo
    {
        static void Bar()
        {
            double d = Math.PI;
        }
    }
}

现在编译器在搜索Outer之前搜索System ,找到System.Math ,一切都很好。

有些人认为Math可能是用户定义类的一个坏名称,因为在System已有一个; 这里要说的就是这样有区别的,它会影响你的代码的可维护性。

注意如果Foo在名称空间Outer中而不是Outer.Inner会发生什么,这也很有趣。在这种情况下,在Outer.Math中添加Outer.Math会破坏Outer.Math ,无论using如何。这意味着编译器在查看任何using指令之前搜索最里面的封闭命名空间。

这个帖子已经有了一些很好的答案,但我觉得我可以通过这个额外的答案带来更多细节。

首先,请记住带有句点的名称空间声明,例如:

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类型,搜索顺序如下:

  1. C嵌套类型(包括继承的嵌套类型)
  2. 当前名称空间MyCorp.TheProduct.SomeModule.Utilities类型
  3. 命名空间MyCorp.TheProduct.SomeModule类型
  4. MyCorp.TheProduct类型。 MyCorp.TheProduct
  5. MyCorp类型
  6. null命名空间中的类型(全局命名空间)
  7. SystemSystem.Collections.GenericSystem.LinqMyCorp.TheProduct.OtherModuleMyCorp.TheProduct.OtherModule.IntegrationThirdParty

另一个惯例:

(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;
    }
}

现在,按以下顺序搜索Ambiguous类型:

  1. C嵌套类型(包括继承的嵌套类型)
  2. 当前名称空间MyCorp.TheProduct.SomeModule.Utilities类型
  3. SystemSystem.Collections.GenericSystem.LinqMyCorp.TheProductMyCorp.TheProduct.OtherModuleMyCorp.TheProduct.OtherModule.IntegrationThirdParty
  4. 命名空间MyCorp.TheProduct.SomeModule类型
  5. MyCorp类型
  6. null命名空间中的类型(全局命名空间)

(注意, MyCorp.TheProduct是 “3.” 的一部分,因此在 “4.” 和 “5.” 之间不需要。)

结束语

无论你是将命令放在命名空间声明的内部还是外部,总有可能以后有人将具有相同名称的新类型添加到具有更高优先级的命名空间之一。

此外,如果嵌套命名空间与类型具有相同的名称,则可能会导致问题。

将使用从一个位置移动到另一个位置总是很危险的,因为搜索层次结构发生了变化,可能会找到另一种类型。因此,选择一个约定并坚持下去,这样你就不必再使用它了。

默认情况下,Visual Studio 的模板将使用放在命名空间之外 (例如,如果您使 VS 在新文件中生成新类)。

在外部使用的一个(微小的)优点是,您可以将 using 指令用于全局属性,例如[assembly: ComVisible(false)]而不是[assembly: System.Runtime.InteropServices.ComVisible(false)]

将它放在命名空间中会使文件的该命名空间的声明本地化(如果文件中有多个命名空间),但是如果每个文件只有一个命名空间,那么无论它们是在外面还是在外面都没有多大区别在命名空间内。

using ThisNamespace.IsImported.InAllNamespaces.Here;

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

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

namespace Namespace3
{ 
   using ThisNamespace.IsImported.InJustNamespace3;
}