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

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


当前回答

当您希望使用别名时,在名称空间中放置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 MyAlias = System.Tuple<System.Linq.Expressions.Expression<System.Func<System.DateTime, object>>, System.Linq.Expressions.Expression<System.Func<System.TimeSpan, object>>>;

不漂亮。

其他回答

根据StyleCop文档:

SA1200:使用方向必须放置在命名空间中

原因C#using指令位于命名空间元素的外部。

规则说明如果将using指令或using别名指令放置在命名空间元素之外,则会违反此规则,除非文件不包含任何命名空间元素。

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

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

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

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

在命名空间元素中放置使用别名指令可以消除这一错误源。

多个命名空间

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

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

如何修复违规要修复违反此规则的问题,请在命名空间元素中移动所有using指令和using别名指令。

正如杰佩·斯蒂格·尼尔森(Jeppe Stig Nielsen)所说,这条线索已经有了很好的答案,但我认为这一相当明显的微妙之处也值得一提。

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

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

根据Hanselman-使用指令和装配加载。。。和其他此类物品在技术上没有区别。

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

尚未提及:

将using指令放在命名空间声明中是众所周知的最佳编程实践的一个应用,即在尽可能小的范围内声明所有内容。

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

这可能是将using指令放在命名空间声明中的最佳理由,而不管其他地方提到的(边界)技术(边界)优点;就这么简单。


已经提到了,但可能更好地说明了:

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

这是不必要的简洁:

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

这是甜蜜的,切中要害:

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

当您希望使用别名时,在名称空间中放置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 MyAlias = System.Tuple<System.Linq.Expressions.Expression<System.Func<System.DateTime, object>>, System.Linq.Expressions.Expression<System.Func<System.TimeSpan, object>>>;

不漂亮。