“使用”语句应内部或外部名称空间? - Should 'using' statements be inside or outside the namespace?

- 此内容更新于:2015-12-16
主题:

我一直StyleCop一些c#代码运行,和它使报告我的语句应该在名称空间中。有技术的原因把using语句内部而不是外部名称空间?

原文:

I have been running StyleCop over some C# code, and it keeps reporting that my using statements should be inside the namespace.

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

网友:有时它使不同位置usings:stackoverflow.com/questions/292535/linq-to-sql-designer-bug

(原文:Sometimes it makes difference where you put usings: stackoverflow.com/questions/292535/linq-to-sql-designer-bug)

网友:仅供参考,有意义不仅仅是每个文件多个类的问题,如果你是新到这个问题,请继续阅读。

(原文:Just for reference, there are implications beyond just the question of multiple classes per file, so if you're new to this question, please keep reading.)

网友:他们应该在哪里我想要他们,我希望他们那里时,只要我想要他们在那里。没有问题问。如果他们没有我的允许移动;你必被删除。

(原文:They should be where ever I want them to be, when I want them to be there, and for as long as I want them to be there. No questions asked. And if they move without my permission; thou shalt be deleted.)

网友:@user-12506——这不能很好地工作在一个中型到大型开发团队一定程度的代码一致性是必需的。如前所述,如果你不明白不同的布局你会发现边界情况不像您预期的那样工作。

(原文:@user-12506 - this does not work very well in a medium to large development team where some level of code consistency is required. And as noted previously, if you don't understand the different layouts you may find edge cases that don't work as you expect.)

网友:@benPearce嘿谢谢:)我每天学习新东西!我还没有机会工作在一个大型开发团队。总有一天……:)

(原文:@benPearce Hey thanks :) I learn something new everyday! I haven't had the opportunity to work in a large dev team yet. Someday... :))

解决方案:
这实际上是一个微妙的两者之间的区别。想象你在File1下面的代码。cs:现在想象有人说另一个文件(File2.cs)这样的项目:编译器搜索之前,看着这些语句以外的名称空间,所以它找到的。不幸的是(或者幸运?),外。数学没有成员,所以File1现在坏了。这改变如果你把里面的使用名称空间声明,如下:现在编译器搜索在搜索外,发现系统。数学,一切都好。有些人认为可能是一个糟糕的名字为一个用户定义的类,因为已经有一个系统,这里的重点是有区别,它会影响你的代码的可维护性。这也是有趣的如果是在名称空间外,而不是。在这种情况下,添加外。数学在File2减免File1不管用。这意味着编译器搜索最内层的封闭名称空间看起来在任何使用语句之前。
原文:

There is actually a (subtle) difference between the two. Imagine you have the following code in 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
namespace Outer
{
    class Math
    {
    }
}

The compiler searches Outer before looking at those using statements outside the namespace, so it finds Outer.Math instead of System.Math. Unfortunately (or perhaps fortunately?), Outer.Math has no PI member, so File1 is now broken.

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

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

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

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

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

网友:imho更好的理由把using语句比马克的本地multiple-namespaces-in-one-file论点。特别是正弦编译会抱怨的命名冲突(参见StyleCop文档这个规则(如Jared发布))。

(原文:This is imho a much better reason to put using statements locally than Mark's multiple-namespaces-in-one-file argument. Especially sine the compile can and will complain about the naming clash (see the StyleCop documentation for this rule (e.g. as posted by Jared)).)

网友:接受答案是好的,但对我来说这似乎是一个很好的理由把使用条款以外的名称空间。如果我在命名空间外。内心的我希望使用数学类外。内心而不是System.Math。

(原文:The accepted answer is good, but to me it seems like a good reason to put the using clauses outside the namespace. If I'm in namespace Outer.Inner I would expect it to use the Math class from Outer.Inner and not System.Math.)

网友:我同意这个。接受的答案是正确的,因为它在技术上描述的区别。然而,一个或另一个类需要明确调出。我非常会ratehr有“数学”解决我自己的本地类,和“系统。数学是指外部类——即使系统。数学被用作“数学”外。数学的存在。是的,这是更多的工作来修复然而许多预先存在的引用,但这也可能暗示也许外。数学应该有一个不同的名字!

(原文:I concur with this as well. The accepted answer is correct in that it technically describes the difference. However, one or the other class will need an explicit callout. I would very much ratehr have "Math" resolve to my own local class, and "System.Math" refer to the external class - even if System.Math was being used as "Math" before Outer.Math existed. Yes, it's more work to fix however many pre-existing references, but that could also be a hint that maybe Outer.Math should have a different name!)

网友:伟大的回答,但在我看来,我只是想把本地非骨架using语句,并保持全球框架使用语句。有人进一步解释为什么我应该完全改变了我的喜好吗?这是从哪里来的,外面的模板在VS2008中使用名称空间?

(原文:Great answer, but it seems to me that I'd only want to put non-framework using statements locally, and keep the framework using statements global. Anyone have further explanation why I should completely changed my preference? Also where did this come from, the templates in VS2008 put using outside the namespace?)

网友:我认为这更多的是一种糟糕的命名约定,而不是改变你使用的地方。不应该有一个类称为数学在您的解决方案

(原文:I think this is more of a bad naming convention rather than changing the place of your using. There shouldn't be a class called Math in your solution)

解决方案:
把它在名称空间的声明本地命名空间的文件(如果你有多个名称空间的文件),但如果你只有一个名称空间每个文件然后它不改变是否外出或在名称空间中。
原文:

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 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;
}
网友:命名空间提供了一个合乎逻辑的分离,而不是物理(文件)。

(原文:namespaces provide a logical separation, not a physical (file) one.)

网友:这不是真的没有区别;指令块内可以引用相对名称空间基于封闭名称空间块。

(原文:It's not quite true that there is no difference; using directives within namespace blocks can refer to relative namespaces based on the enclosing namespace block.)

网友:是的,我知道。我们建立了,在五年前接受回答这个问题。

(原文:yeah I know. we established that in this question's accepted answer five years ago.)

解决方案:
这个线程已经有一些很棒的答案,但是我觉得我可以用这个额外带来一些更详细的答案。首先,记住时间的名称空间声明,如:完全等价于:如果你想,你可以给所有这些指令的水平。(当然,我们想要usings只在一个地方,但它将法律根据语言。)类型是隐含的规则来解决,可以松散表示:首先搜索最隐秘的“范围”匹配,如果没有发现出去有一个水平到另一个范围和搜索,等等,直到找到一个匹配。如果在某种程度上多个匹配,如果其中一个类型从当前装配,选择这一问题一个编译器警告。否则,放弃(编译时错误)。现在,让我们明白…这意味着与两个主要约定一个具体的例子。usings外(1):在上述情况下,找出什么类型,搜索是按照这个顺序的:内嵌套类型(包括继承的嵌套类型)类型在当前命名空间类型的空名称空间中的名称空间类型类型的类型(全局命名空间)类型,,,,,和其他约定:(2)里面有usings:现在,寻找模糊类型是按照这个顺序的:内嵌套类型C(包括继承的嵌套类型)MyCorp.TheProduct.SomeModule类型在当前的名称空间。公用事业类型系统,System.Collections。通用的,系统。Linq,MyCorp。产品,MyCorp.TheProduct。OtherModuleMyCorp.TheProduct.OtherModule。集成和第三方MyCorp.TheProduct类型名称空间。SomeModule类型MyCorp类型的空名称空间(全局命名空间)(注意,MyCorp。产品是一个“3的一部分。“之间”,因此不需要4。”和“5”)。结论无论如果你把usings内部或外部名称空间声明,总会有人的可能性之后添加一个新类型具有相同名称的一个名称空间具有更高的优先级。同样,如果一个嵌套名称空间具有相同的名称作为一个类型,它会引起问题。总是危险usings从一个位置移动到另一个,因为搜索层次变化,和另一种类型可能被发现。因此,选择一个公约并坚持下去,这样你就不用usings移动。VisualStudio的模板,默认情况下,把usings以外的名称空间(例如,如果你对一个新文件中生成一个新类)。(小)usings之外的优势在于你可以利用全球属性的使用指令,例如代替。
原文:

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 using directives 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.)

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:

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 Ambiguous is, the search goes in this order:

  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 null namespace (the global namespace)
  7. Types in System, System.Collections.Generic, System.Linq, MyCorp.TheProduct.OtherModule, MyCorp.TheProduct.OtherModule.Integration, and ThirdParty

The other convention:

(2) With usings 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 Ambiguous goes in this order:

  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 null namespace (the global namespace)

(Note that MyCorp.TheProduct was a part of "3." and was therefore not needed between "4." and "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.

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 outside of the namespace (for example if you make VS generate a new class in a new file).

One (tiny) advantage of having usings outside is 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)].

网友:谢谢,这是一个比接受更好的解释回答。

(原文:thanks , this is a much better explanation than the accepted answer.)

解决方案:
根据Hanselman-使用指令和组装加载…等文章在技术上没有区别。我喜欢把它们之外的名称空间。
原文:

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

My preference is to put them outside of namespaces.

网友:@ChrisM:嗯…答案显示的链接没有好处和,实际上显示一个伪造的例子所做的联系你了…

(原文:@Chris M: uh... the link posted in the answer indicates there's no benefit to in vs. out, actually showing an example that falsifies the claim made in the link you posted...)

网友:啊我没有完全读线程但在mvp时买的说它是对的。一个人否定了它,解释它,进一步显示了他的代码…“c#编译器生成的IL在这两种情况下是相同的。事实上,c#编译器生成精确没有相应使用指令。使用指令纯粹是一个c#ism,他们没有意义。网络本身。(不正确使用语句,但这些都是完全不同的东西。)“groups.google.com/group/wpf-disciples/msg/781738deb0a15c46

(原文:Aye I didn't fully read the thread but bought in when the MVPs said it was right. A guy disproves it, explains it and shows his code further down... "The IL that the C# compiler generates is the same in either case. In fact the C# compiler generates precisely nothing corresponding to each using directive. Using directives are purely a C#ism, and they have no meaning to .NET itself. (Not true for using statements but those are something quite different.)" groups.google.com/group/wpf-disciples/msg/781738deb0a15c46)

网友:请包括一个链接的总结。链接时坏了(因为它会发生,给予足够的时间),与32个问题只值突然一个答案——几乎没有一个答案。

(原文:Please include a summary of the link. When the link is broken (because it will happen, given enough time), suddenly an answer with 32 upvotes is only worth My style is to put them outside the namespaces. - barely an answer at all.)

解决方案:
根据StyleCop文档:SA1200:UsingDirectivesMustBePlacedWithinNamespace导致c#使用指令外放置一个名称空间元素。规则描述违反这条规则发生在使用指令或使用别名指令外放置一个名称空间元素,除非文件不包含任何名称空间的元素。例如,下面的代码将导致两个违反这条规则。然而,下面的代码,不会导致任何违反这条规则:这个代码可以编译,干净,没有任何编译器错误。然而,目前尚不清楚哪个版本的Guid类型被分配。如果使用指令名称空间内移动,如下所示,将发生一个编译器错误:代码失败第二编译错误,发现含有CS0576行:名称空间的微软。示例包含一个定义冲突的别名的Guid的系统代码创建一个别名。Guid类型称为Guid,并创建自己的类型称为Guid与匹配的构造函数接口。后来,Guid类型的代码创建了一个实例。创建这个实例,编译器必须选择之间的两种不同定义的Guid。当使用别名指令被放置在名称空间的元素,编译器会选择当地的Guid本地命名空间中定义的定义,和完全忽视使用别名指令定义之外的名称空间。不幸的是,这是阅读代码时不明显。当使用别名指令名称空间中的定位,然而,编译器必须选择两个不同的,相互矛盾的Guid类型都定义在相同的名称空间中。这两种类型提供一个匹配的构造函数。编译器无法做出决定,所以它标志编译器错误。将使用别名名称空间之外的指令是一个糟糕的做法,因为它会导致这种混乱的情况下,不明显的地方哪个版本的类型实际上是被使用。这可能会导致一个错误可能很难诊断。将使用别名指令名称空间中的元素可以消除这个错误的来源。多个名称空间放置多个名称空间的元素在一个文件中通常是一个坏主意,但如果这样做,这是一个好主意的地方所有使用指令在每个名称空间的元素,而不是全球文件的顶部。这将范围名称空间,也将有助于避免上述的行为。重要的是要注意,当使用指令代码已经编写外的名称空间,应该小心当移动这些指令在命名空间内,以确保这不是改变代码的语义。像前面解释的那样,使用别名指令名称空间中的元素允许编译器选择冲突类型的方式不会发生时,指令被放置在名称空间中。如何修复违反修复违反这条规则,将所有使用指示和使用别名指令名称空间中的元素。
原文:

According the to StyleCop Documentation:

SA1200: UsingDirectivesMustBePlacedWithinNamespace

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

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.

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:

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");

CS0576: Namespace 'Microsoft.Sample' contains a definition conflicting with alias '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.

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.

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.

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

  1. Multiple Namespaces

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.

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.

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

网友:@Jared——我注意到在我的答案,我优先解决方法/解决方案是每个文件只有一个类。我认为这是一个相当常见的惯例。

(原文:@Jared - as I noted in my answer, my prefered workaround / solution is to only ever have one class per file. I think that this is a fairly common convention.)

网友:事实上,这也是一个StyleCop规则!SA1402:c#文档可能只包含一个类在根级别,除非所有的类都是部分相同类型的。展示一个规则打破另一个错误的汁滴。

(原文:Indeed, it's also a StyleCop rule! SA1402: A C# document may only contain a single class at the root level unless all of the classes are partial and are of the same type. Showcasing one rule by breaking another just drips with wrong sauce.)

网友:Upvoted成为第一个答案实际上涵盖从StyleCop视角。我个人喜欢的视觉感觉之外的名称空间。内部usings看起来很丑陋。:)

(原文:Upvoted for being the first answer to actually cover it from the StyleCop perspective. Personally I like the visual feel of usings outside the namespace. Inner usings looks so ugly to me. :))

解决方案:
有一个问题在名称空间中放置using语句当您希望使用别名。别名不受益于早期的语句和必须完全合格。考虑:和:这可能是特别显著,如果你有一个冗长的别名,如以下(这是我发现的问题):使用语句内的名称空间,它突然变成了:不漂亮。
原文:

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 using statements and has to be fully qualified.

Consider:

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

    class
    {
    }
}

versus:

namespace MyNamespace
{
    using System;

    class
    {
        using MyAlias = DateTime;
    }
}

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 using statements inside the namespace, it suddenly becomes:

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

Not pretty.

网友:确实不漂亮……

(原文:Not pretty indeed...)

解决方案:
更好的做法是如果使用即默认。“引用”用在你的源代码解决方案应该在名称空间和那些“新添加引用”是一个很好的实践是你应该把它在名称空间。这是区分什么引用补充道。
原文:

It is a better practice if those default using 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.

网友:不,实际上这是一个糟糕的主意。你不应该基地之间的位置在本地范围内和全球范围内的使用指令是新添加的。相反,它是良好的实践用字母表示,除了基类库引用,应该在上面。

(原文:No, actually that is a bad idea. You should not base the location between locally scoped and globally scoped of using directives on the fact that they are newly added or not. Instead, it is good practice to alphabetize them, except for BCL references, which should go on top.)