我一直在一些C#代码上运行StyleCop,它不断报告我的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
    {
    }
}

编译器在查看命名空间外使用指令的对象之前搜索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;
        }
    }
}

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

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

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

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

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

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

正如杰佩·斯蒂格·尼尔森(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语句中受益,必须完全限定。

考虑:

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

不漂亮。