应该“使用” C# 中的指令位于命名空间内部还是外部?

发布于 2024-11-02 13:06:44 字数 210 浏览 2 评论 0原文

我一直在一些 C# 代码上运行 StyleCop ,并且它不断报告我的 using< /code> 指令应该位于命名空间内。

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

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(15

愚人国度 2024-11-09 13:06:44

两者之间实际上存在(微妙的)差异。假设您在 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,然后使用 指令位于命名空间之外,因此它会查找 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;
        }
    }
}

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

有些人会认为,对于用户定义的类来说,Math 可能是一个不好的名字,因为 System 中已经有一个这样的名称;这里的要点是存在差异,它会影响代码的可维护性。

值得注意的是,如果 Foo 位于命名空间 Outer 中,而不是 Outer.Inner 中,会发生什么情况。在这种情况下,无论 using 位于何处,在 File2 中添加 Outer.Math 都会破坏 File1。这意味着编译器在查看任何 using 指令之前会先搜索最内层的封闭命名空间。

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 directives 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 directive.

戏舞 2024-11-09 13:06:44

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

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

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

完全等同于:

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

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

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

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

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

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

  1. C 内部的嵌套类型code> (包括继承的嵌套类型)
  2. 当前命名空间中的类型 MyCorp.TheProduct.SomeModule.Utilities
  3. 命名空间中的类型 MyCorp.TheProduct.SomeModule
  4. 中的类型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;
    }
}

现在,搜索类型 Ambigitude 按以下顺序进行:

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

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

结束语

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

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

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

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

外部使用 using 的一个(微小)优点是,您可以将 using 指令用于全局属性,例如 [assemble: ComVisible(false)] 而不是 <代码>[程序集:System.Runtime.InteropServices.ComVisible(false)]。


其他线程启发的添加:假设在上面的示例中,命名空间MyCorp.TheProduct.System发生了存在,即使我们的文件中没有使用它。然后在外面使用,它不会改变任何东西。但是使用 usings inside 时,您必须使用 global 别名,如下所示:

namespace MyCorp.TheProduct.SomeModule.Utilities
{
    using global::System;  // avoids 'MyCorp.TheProduct.System' which also exists
    using global::System.Collections.Generic;
    // etc.
}

更新有关文件范围的命名空间声明

自 C# 10.0(从2021),您可以避免缩进并使用(约定 1,外部使用):

using System;
using System.Collections.Generic;
using System.Linq;
using MyCorp.TheProduct.OtherModule;
using MyCorp.TheProduct.OtherModule.Integration;
using ThirdParty;

namespace MyCorp.TheProduct.SomeModule.Utilities;

class C
{
    Ambiguous a;
}

或(约定 2,内部使用):

namespace MyCorp.TheProduct.SomeModule.Utilities;

using System;
using System.Collections.Generic;
using System.Linq;
using MyCorp.TheProduct;
using MyCorp.TheProduct.OtherModule;
using MyCorp.TheProduct.OtherModule.Integration;
using ThirdParty;

class C
{
    Ambiguous a;
}

但与之前的注意事项相同。

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)].


Addition inspired by other thread: Suppose in the above example that the namespace MyCorp.TheProduct.System happened to exist, even though we have no use for it in our file. Then with usings outside, it would not change anything. But with usings inside, you would have to use the global alias, like this:

namespace MyCorp.TheProduct.SomeModule.Utilities
{
    using global::System;  // avoids 'MyCorp.TheProduct.System' which also exists
    using global::System.Collections.Generic;
    // etc.
}

Update about file-scoped namespace declarations

Since C# 10.0 (from 2021), you can avoid indentation and use either (convention 1, usings outside):

using System;
using System.Collections.Generic;
using System.Linq;
using MyCorp.TheProduct.OtherModule;
using MyCorp.TheProduct.OtherModule.Integration;
using ThirdParty;

namespace MyCorp.TheProduct.SomeModule.Utilities;

class C
{
    Ambiguous a;
}

or (convention 2, usings inside):

namespace MyCorp.TheProduct.SomeModule.Utilities;

using System;
using System.Collections.Generic;
using System.Linq;
using MyCorp.TheProduct;
using MyCorp.TheProduct.OtherModule;
using MyCorp.TheProduct.OtherModule.Integration;
using ThirdParty;

class C
{
    Ambiguous a;
}

But the same considerations as before apply.

乜一 2024-11-09 13:06:44

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

using ThisNamespace.IsImported.InAllNamespaces.Here;

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

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

namespace Namespace3
{ 
   using ThisNamespace.IsImported.InJustNamespace3;
}

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;
}
残疾 2024-11-09 13:06:44

根据 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.

霞映澄塘 2024-11-09 13:06:44

根据 StyleCop 文档:

SA1200:UsingDirectivesMustBePlacedWithinNamespace

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

规则说明
当 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");< 的行上/code>

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

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

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

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

将 using-alias 指令放置在命名空间元素中可以消除这种错误来源。

  1. 多个命名空间

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

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

如何纠正违规行为
要修复违反此规则的问题,请移动命名空间元素内的所有 using 指令和 using-alias 指令。

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.

我不是你的备胎 2024-11-09 13:06:44

当您希望使用别名时,在命名空间内放置 using 语句会出现问题。该别名无法从早期的 using 语句中受益,并且必须是完全限定的。

考虑:

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

    class MyClass
    {
    }
}

与:

using System;

namespace MyNamespace
{
    using MyAlias = DateTime;

    class MyClass
    {
    }
}

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

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

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

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

:漂亮的。

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

流星番茄 2024-11-09 13:06:44

我遇到的一个问题(其他答案中没有涵盖):

假设您有这些命名空间:

  • Something.Other
  • Parent.Something.Other

当您使用 using Something.Other outside 命名空间Parent,它引用第一个(Something.Other)。

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

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

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

One wrinkle I ran into (that isn't covered in other answers):

Suppose you have these namespaces:

  • Something.Other
  • Parent.Something.Other

When you use using Something.Other outside of a namespace Parent, it refers to the first one (Something.Other).

However if you use it inside of that namespace declaration, it refers to the second one (Parent.Something.Other)!

There is a simple solution: add the "global::" prefix: docs

namespace Parent
{
   using global::Something.Other;
   // etc
}
小霸王臭丫头 2024-11-09 13:06:44

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

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

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

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

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

//file3.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.
        }
    }
}
友欢 2024-11-09 13:06:44

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

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

以下示例之所以有效,是因为类型 FooBar 都位于同一全局命名空间 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;
    }
}

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

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

The following example works because the types Foo and Bar are both in the same global namespace, Outer.

Presume the code file Foo.cs:

namespace Outer.Inner
{
    class Foo { }
}

And Bar.cs:

namespace Outer
{
    using Outer.Inner;

    class Bar
    {
        public Foo foo;
    }
}

That may omit the outer namespace in the using directive, for short:

namespace Outer
{
    using Inner;

    class Bar
    {
        public Foo foo;
    }
}
绾颜 2024-11-09 13:06:44
尚未提及:

using 指令放入命名空间声明内部是众所周知的最佳编程实践的应用在尽可能小的范围内声明所有内容

如果最佳编程实践是您的第二天性,那么您会自动执行类似的操作。

这可能是将您的 using 指令放入名称空间声明中的最佳理由,无论其他地方提到的(边界)技术(边界)优点如何;就这么简单。


已经提到过,但也许更好地说明了:

using 指令放置在命名空间内可以避免不必要的重复,并使我们的声明更加简洁。

这是不必要的简洁:

using Com.Acme.Products.Traps.RoadRunnerTraps;
namespace Com.Acme.Products.Traps {

这是甜蜜且切题的:

namespace Com.Acme.Products.Traps { 
using RoadRunnerTraps;
Not already mentioned:

Placing the using directives inside the namespace declaration is an application of the well-known best programming practice of declaring everything in the smallest scope possible.

If best programming practices are second nature to you, then you do things like that automatically.

This might be the best reason for putting your using directives inside the namespace declaration, regardless of (borderline) technical (borderline) merits mentioned elsewhere; It's as simple as that.


Already mentioned but perhaps better illustrated:

Placing using directives inside the namespace avoids unnecessary repetition and makes our declarations more terse.

This is unnecessarily terse:

using Com.Acme.Products.Traps.RoadRunnerTraps;
namespace Com.Acme.Products.Traps {

This is sweet and to the point:

namespace Com.Acme.Products.Traps { 
using RoadRunnerTraps;
千鲤 2024-11-09 13:06:44

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

可以通过添加来调整 stylecop 以检查命名空间之外的 using 指令项目文件根目录中的 stylecop.json 文件包含以下内容:

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

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

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 big and there are tradeoffs for both of them. Visual Studio's default template for creating .cs files use using directives outside of namespaces e.g.

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

{
  "$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.

末蓝 2024-11-09 13:06:44

通常,外部 using 指令(例如 System 和 Microsoft 命名空间)应放置在 namespace 指令的外部。它们是默认值,应在所有情况下应用除非另有说明。这应该包括您自己组织的任何不属于当前项目的内部库,或者引用同一项目中其他主命名空间的 using 指令。任何引用当前项目和命名空间中其他模块的 using 指令都应放置在 namespace 指令内部。这有两个特定的功能:

  • 它提供本地模块和“其他”模块(意味着其他所有模块)之间的视觉区别。
  • 它限制了本地指令的应用范围,使其优先优于全局指令。

后一个原因很重要。这意味着引入不明确的参考问题会更困难,而这种问题可以通过不比重构代码更重要的更改来引入。也就是说,您将一种方法从一个文件移动到另一个文件,然后突然出现一个以前不存在的错误。通俗地说,“heisenbug”——历史上极难追踪。

As a rule, external using directives (System and Microsoft namespaces for example) should be placed outside the namespace directive. They are defaults that should be applied in all cases unless otherwise specified. This should include any of your own organization's internal libraries that are not part of the current project, or using directives that reference other primary namespaces in the same project. Any using directives that reference other modules in the current project and namespace should be placed inside the namespace directive. This serves two specific functions:

  • It provides a visual distinction between local modules and 'other' modules, meaning everything else.
  • It scopes the local directives to be applied preferentially over global directives.

The latter reason is significant. It means that it's harder to introduce an ambiguous reference issue that can be introduced by a change no more significant than refactoring code. That is to say, you move a method from one file to another and suddenly a bug shows up that wasn't there before. Colloquially, a 'heisenbug' - historically fiendishly difficult to track down.

当梦初醒 2024-11-09 13:06:44

这取决于。
根据 StyleCop 的说法,里面。
据微软称,外部。

(只是想要一个真正简短的答案,我阅读了整个线程,但并不是每个人都有时间这样做)

It depends.
According to StyleCop, inside.
According to Microsoft, outside.

(Just wanted a real short answer, I read the entire thread, but not everyone has time for that)

笑看君怀她人 2024-11-09 13:06:44

虽然大多数答案倾向于将 using 指令放置在 namespace 声明中,但有趣的是,微软在 C# 编码约定 另有说明:

将 using 指令放在命名空间声明之外

当 using 指令位于命名空间声明之外时,
导入的命名空间是其完全限定名称。这样就更清楚了。
当 using 指令位于命名空间内时,它可以是
相对于该名称空间或其完全限定名称。那是
模棱两可。

然而,在这篇文章中,他们规定相反:

Using 语句应该位于命名空间声明内。

While most answers lean towards placing using directives inside the namespace declaration, it is interesting to see that Microsoft's recommendation in C# Coding Conventions says otherwise:

Place the using directives outside the namespace declaration

When a using directive is outside a namespace declaration, that
imported namespace is its fully qualified name. That's more clear.
When the using directive is inside the namespace, it could be either
relative to that namespace or it's fully qualified name. That's
ambiguous.

In this other post however, they prescribe the opposite:

Using statements should be inside the namespace declaration.

笑忘罢 2024-11-09 13:06:44

如果源解决方案中使用的默认(即“引用”)应该位于命名空间之外,并且“新添加的引用”< /strong> 是一个很好的做法,您应该将其放在名称空间内。这是为了区分正在添加的引用。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文