C# 'using' 指令应该在命名空间内部还是外部?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/125319/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 14:43:04  来源:igfitidea点击:

Should 'using' directives be inside or outside the namespace?

提问by benPearce

I have been running StyleCopover some C# code, and it keeps reporting that my usingdirectives should be inside the namespace.

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

Is there a technical reason for putting the usingdirectives inside instead of outside the namespace?

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

采纳答案by Charlie

There is actually a (subtle) difference between the two. Imagine you have the following code in File1.cs:

两者之间实际上存在(微妙的)差异。假设您在 File1.cs 中有以下代码:

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

Now imagine that someone adds another file (File2.cs) to the project that looks like this:

现在假设有人向项目中添加了另一个文件 (File2.cs),如下所示:

// File2.cs
namespace Outer
{
    class Math
    {
    }
}

The compiler searches Outerbefore looking at those usingdirectives outside the namespace, so it finds Outer.Mathinstead of System.Math. Unfortunately (or perhaps fortunately?), Outer.Mathhas no PImember, so File1 is now broken.

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

This changes if you put the usinginside your namespace declaration, as follows:

如果将 放入using命名空间声明中,则会发生变化,如下所示:

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

Now the compiler searches Systembefore searching Outer, finds System.Math, and all is well.

现在编译器先搜索System再搜索Outer,找到System.Math,一切正常。

Some would argue that Mathmight be a bad name for a user-defined class, since there's already one in System; the point here is just that there isa difference, and it affects the maintainability of your code.

有些人会争辩说,Math这对于用户定义的类来说可能是个坏名字,因为System; 这里要说的就是这样存在差异,并且它会影响您的代码的可维护性。

It's also interesting to note what happens if Foois in namespace Outer, rather than Outer.Inner. In that case, adding Outer.Mathin File2 breaks File1 regardless of where the usinggoes. This implies that the compiler searches the innermost enclosing namespace before it looks at any usingdirective.

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

回答by Mark Cidade

Putting it inside the namespaces makes the declarations local to that namespace for the file (in case you have multiple namespaces in the file) but if you only have one namespace per file then it doesn't make much of a difference whether they go outside or inside the namespace.

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

using ThisNamespace.IsImported.InAllNamespaces.Here;

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

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

namespace Namespace3
{ 
   using ThisNamespace.IsImported.InJustNamespace3;
}

回答by Quintin Robinson

According to Hanselman - Using Directive and Assembly Loading...and other such articles there is technically no difference.

根据Hanselman - Using Directive and Assembly Loading...和其他此类文章在技术上没有区别。

My preference is to put them outside of namespaces.

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

回答by JaredCacurak

According the to StyleCop Documentation:

根据 StyleCop 文档:

SA1200: UsingDirectivesMustBePlacedWithinNamespace

SA1200:UsingDirectivesMustBePlacedWithinNamespace

Cause A C# using directive is placed outside of a namespace element.

原因 AC# using 指令放置在命名空间元素之外。

Rule Description A violation of this rule occurs when a using directive or a using-alias directive is placed outside of a namespace element, unless the file does not contain any namespace elements.

规则描述 将 using 指令或 using-alias 指令放置在命名空间元素之外时会违反此规则,除非文件不包含任何命名空间元素。

For example, the following code would result in two violations of this rule.

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

using System;
using Guid = System.Guid;

namespace Microsoft.Sample
{
    public class Program
    {
    }
}

The following code, however, would not result in any violations of this rule:

但是,以下代码不会导致违反此规则:

namespace Microsoft.Sample
{
    using System;
    using Guid = System.Guid;

    public class Program
    {
    }
}

This code will compile cleanly, without any compiler errors. However, it is unclear which version of the Guid type is being allocated. If the using directive is moved inside of the namespace, as shown below, a compiler error will occur:

此代码将编译干净,没有任何编译器错误。但是,目前还不清楚正在分配哪个版本的 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");
        }
    }
}

The code fails on the following compiler error, found on the line containing Guid g = new Guid("hello");

代码因以下编译器错误而失败,在包含的行中找到 Guid g = new Guid("hello");

CS0576: Namespace 'Microsoft.Sample' contains a definition conflicting with alias 'Guid'

CS0576:命名空间“Microsoft.Sample”包含与别名“Guid”冲突的定义

The code creates an alias to the System.Guid type called Guid, and also creates its own type called Guid with a matching constructor interface. Later, the code creates an instance of the type Guid. To create this instance, the compiler must choose between the two different definitions of Guid. When the using-alias directive is placed outside of the namespace element, the compiler will choose the local definition of Guid defined within the local namespace, and completely ignore the using-alias directive defined outside of the namespace. This, unfortunately, is not obvious when reading the code.

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

When the using-alias directive is positioned within the namespace, however, the compiler has to choose between two different, conflicting Guid types both defined within the same namespace. Both of these types provide a matching constructor. The compiler is unable to make a decision, so it flags the compiler error.

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

Placing the using-alias directive outside of the namespace is a bad practice because it can lead to confusion in situations such as this, where it is not obvious which version of the type is actually being used. This can potentially lead to a bug which might be difficult to diagnose.

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

Placing using-alias directives within the namespace element eliminates this as a source of bugs.

在命名空间元素中放置 using-alias 指令可以消除作为错误来源的这一点。

  1. Multiple Namespaces
  1. 多个命名空间

Placing multiple namespace elements within a single file is generally a bad idea, but if and when this is done, it is a good idea to place all using directives within each of the namespace elements, rather than globally at the top of the file. This will scope the namespaces tightly, and will also help to avoid the kind of behavior described above.

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

It is important to note that when code has been written with using directives placed outside of the namespace, care should be taken when moving these directives within the namespace, to ensure that this is not changing the semantics of the code. As explained above, placing using-alias directives within the namespace element allows the compiler to choose between conflicting types in ways that will not happen when the directives are placed outside of the namespace.

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

How to Fix Violations To fix a violation of this rule, move all using directives and using-alias directives within the namespace element.

如何修复违规 要修复违反此规则的情况,请移动命名空间元素内的所有 using 指令和 using-alias 指令。

回答by Neo

There is an issue with placing using statements inside the namespace when you wish to use aliases. The alias doesn't benefit from the earlier usingstatements and has to be fully qualified.

当您希望使用别名时,在命名空间内放置 using 语句存在问题。别名不会从前面的using语句中受益,并且必须是完全限定的。

Consider:

考虑:

namespace MyNamespace
{
    using System;
    using MyAlias = System.DateTime;

    class MyClass
    {
    }
}

versus:

相对:

using System;

namespace MyNamespace
{
    using MyAlias = DateTime;

    class MyClass
    {
    }
}

This can be particularly pronounced if you have a long-winded alias such as the following (which is how I found the problem):

如果您有一个冗长的别名,例如以下(这就是我发现问题的方式),这可能特别明显:

using MyAlias = Tuple<Expression<Func<DateTime, object>>, Expression<Func<TimeSpan, object>>>;

With usingstatements inside the namespace, it suddenly becomes:

使用using命名空间内的语句,它突然变成:

using MyAlias = System.Tuple<System.Linq.Expressions.Expression<System.Func<System.DateTime, object>>, System.Linq.Expressions.Expression<System.Func<System.TimeSpan, object>>>;

Not pretty.

不漂亮。

回答by Jeppe Stig Nielsen

This thread already has some great answers, but I feel I can bring a little more detail with this additional answer.

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

First, remember that a namespace declaration with periods, like:

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

namespace MyCorp.TheProduct.SomeModule.Utilities
{
    ...
}

is entirely equivalent to:

完全等同于:

namespace MyCorp
{
    namespace TheProduct
    {
        namespace SomeModule
        {
            namespace Utilities
            {
                ...
            }
        }
    }
}

If you wanted to, you could put usingdirectives on all of these levels. (Of course, we want to have usings in only one place, but it would be legal according to the language.)

如果您愿意,可以using在所有这些级别上放置指令。(当然,我们只想using在一个地方有s,但根据语言是合法的。)

The rule for resolving which type is implied, can be loosely stated like this: First search the inner-most "scope" for a match, if nothing is found there go out one level to the next scope and search there, and so on, until a match is found. If at some level more than one match is found, if one of the types are from the current assembly, pick that one and issue a compiler warning. Otherwise, give up (compile-time error).

解析隐含类型的规则可以大致如下:首先搜索最内部的“范围”以查找匹配项,如果在那里找不到任何内容,则转到下一个范围并在那里搜索,依此类推,直到找到匹配项。如果在某个级别找到多个匹配项,并且其中一种类型来自当前程序集,则选择该类型并发出编译器警告。否则,放弃(编译时错误)。

Now, let's be explicit about what this means in a concrete example with the two major conventions.

现在,让我们在具有两个主要约定的具体示例中明确说明这意味着什么。

(1) With usings outside:

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

In the above case, to find out what type Ambiguousis, the search goes in this order:

在上述情况下,要找出是什么类型Ambiguous,搜索按以下顺序进行:

  1. Nested types inside C(including inherited nested types)
  2. Types in the current namespace MyCorp.TheProduct.SomeModule.Utilities
  3. Types in namespace MyCorp.TheProduct.SomeModule
  4. Types in MyCorp.TheProduct
  5. Types in MyCorp
  6. Types in the nullnamespace (the global namespace)
  7. Types in System, System.Collections.Generic, System.Linq, MyCorp.TheProduct.OtherModule, MyCorp.TheProduct.OtherModule.Integration, and ThirdParty
  1. 内部嵌套类型C(包括继承的嵌套类型)
  2. 当前命名空间中的类型 MyCorp.TheProduct.SomeModule.Utilities
  3. 命名空间中的类型 MyCorp.TheProduct.SomeModule
  4. 输入 MyCorp.TheProduct
  5. 输入 MyCorp
  6. 在类型命名空间(全局命名空间)
  7. 类型SystemSystem.Collections.GenericSystem.LinqMyCorp.TheProduct.OtherModuleMyCorp.TheProduct.OtherModule.Integration,和ThirdParty

The other convention:

另一个约定:

(2) With usings inside:

(2) with using inside:

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

Now, search for the type Ambiguousgoes in this order:

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

  1. Nested types inside C(including inherited nested types)
  2. Types in the current namespace MyCorp.TheProduct.SomeModule.Utilities
  3. Types in System, System.Collections.Generic, System.Linq, MyCorp.TheProduct, MyCorp.TheProduct.OtherModule, MyCorp.TheProduct.OtherModule.Integration, and ThirdParty
  4. Types in namespace MyCorp.TheProduct.SomeModule
  5. Types in MyCorp
  6. Types in the nullnamespace (the global namespace)
  1. 内部嵌套类型C(包括继承的嵌套类型)
  2. 当前命名空间中的类型 MyCorp.TheProduct.SomeModule.Utilities
  3. 输入System, System.Collections.Generic, System.Linq, MyCorp.TheProduct, MyCorp.TheProduct.OtherModule, MyCorp.TheProduct.OtherModule.Integration, 和ThirdParty
  4. 命名空间中的类型 MyCorp.TheProduct.SomeModule
  5. 输入 MyCorp
  6. 在类型命名空间(全局命名空间)

(Note that MyCorp.TheProductwas a part of "3." and was therefore not needed between "4." and "5.".)

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

Concluding remarks

结束语

No matter if you put the usings inside or outside the namespace declaration, there's always the possibility that someone later adds a new type with identical name to one of the namespaces which have higher priority.

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

Also, if a nested namespace has the same name as a type, it can cause problems.

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

It is always dangerous to move the usings from one location to another because the search hierarchy changes, and another type may be found. Therefore, choose one convention and stick to it, so that you won't have to ever move usings.

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

Visual Studio's templates, by default, put the usings outsideof the namespace (for example if you make VS generate a new class in a new file).

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

One (tiny) advantage of having usings outsideis that you can then utilize the using directives for a global attribute, for example [assembly: ComVisible(false)]instead of [assembly: System.Runtime.InteropServices.ComVisible(false)].

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

回答by Israel Ocbina

It is a better practice if those defaultusing i.e. "references" used in your source solution should be outside the namespaces and those that are "new added reference"is a good practice is you should put it inside the namespace. This is to distinguish what references are being added.

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

回答by Biscuits

As Jeppe Stig Nielsen said, this thread already has great answers, but I thought this rather obvious subtlety was worth mentioning too.

正如 Jeppe Stig Nielsen所说,这个帖子已经有了很好的答案,但我认为这个相当明显的微妙之处也值得一提。

usingdirectives specified inside namespaces can make for shorter code since they don't need to be fully qualified as when they're specified on the outside.

using在命名空间内指定的指令可以使代码更短,因为它们不需要像在外部指定时那样完全限定。

The following example works because the types Fooand Barare both in the same global namespace, Outer.

以下示例有效,因为类型FooBar都在同一个全局命名空间Outer.

Presume the code file Foo.cs:

假设代码文件Foo.cs

namespace Outer.Inner
{
    class Foo { }
}

And Bar.cs:

Bar.cs

namespace Outer
{
    using Outer.Inner;

    class Bar
    {
        public Foo foo;
    }
}

That may omit the outer namespace in the usingdirective, for short:

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

namespace Outer
{
    using Inner;

    class Bar
    {
        public Foo foo;
    }
}

回答by sotn

The technical reasons are discussed in the answers and I think that it comes to the personal preferences in the end since the difference is not that bigand there are tradeoffs for both of them. Visual Studio's default template for creating .csfiles use usingdirectives outside of namespaces e.g.

答案中讨论了技术原因,我认为最终取决于个人喜好,因为差异并不大,而且两者都有权衡。Visual Studio 用于创建.cs文件的默认模板使用using命名空间之外的指令,例如

One can adjust stylecop to check usingdirectives outside of namespaces through adding stylecop.jsonfile in the root of the project file with the following:

可以using通过stylecop.json在项目文件的根目录中添加文件来调整 stylecop 以检查命名空间之外的指令,如下所示:

{
  "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
    "orderingRules": {
      "usingDirectivesPlacement": "outsideNamespace"
    }
  }
}

You can create this config file in solution level and add it to your projects as 'Existing Link File' to share the config across all of your projects too.

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

回答by Ben Gardner

Another subtlety that I don't believe has been covered by the other answers is for when you have a class and namespace with the same name.

我认为其他答案没有涵盖的另一个微妙之处是当您有一个同名的类和命名空间时。

When you have the import inside the namespace then it will find the class. If the import is outside the namespace then the import will be ignored and the class and namespace have to be fully qualified.

当您在命名空间中导入时,它将找到该类。如果导入在命名空间之外,那么导入将被忽略并且类和命名空间必须是完全限定的。

//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.
        }
    }
}