'using'ディレクティブは名前空間の内部または外部にある必要がありますか?


2062

私はいくつかのC#コードでStyleCopを実行しており、usingディレクティブが名前空間内にある必要があると報告し続けます。

usingディレクティブを名前空間の外ではなく内部に配置する技術的な理由はありますか?


4
:時にはそれはあなたがusings入れ違いますstackoverflow.com/questions/292535/linq-to-sql-designer-bug
gius

82
参考までに、ファイルごとに複数のクラスを使用するだけでなく、この問題に慣れていない場合は、読み続けてください。
チャーリー

3
@ user-12506-これは、ある程度のコードの一貫性が要求される中規模から大規模の開発チームではあまりうまく機能しません。また、前述のように、さまざまなレイアウトを理解していないと、期待どおりに機能しないエッジケースが見つかる場合があります。
benPearce 14

35
用語:これらはusing ステートメントではありません。それらはusing ディレクティブです。usingステートメントが、一方、例として、メソッド本体等内の他の文と一緒に発生する言語構造であり、using (var e = s.GetEnumerator()) { /* ... */ }緩く同じであるステートメントですvar e = s.GetEnumerator(); try { /* ... */ } finally { if (e != null) { e.Dispose(); } }
Jeppe Stig Nielsen

1
これがまだ誰にも言及されていない場合、実際にはマイクロソフトも宣言using内の内部コーディングガイドラインにステートメントを配置することを推奨していますnamespace
user1451111

回答:


2133

実際には、2つの間に(微妙な)違いがあります。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それらのusingディレクティブを調べる前に検索するため、の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すでに1つあるので、ユーザー定義クラスの名前としては不適切だと主張する人もいSystemます。ここでのポイント、違いあるということだけであり、コードの保守性に影響します。

FooがでOuterはなく名前空間にある場合に何が起こるかを確認することも興味深いですOuter.Inner。その場合、Outer.MathFile2 に追加すると、場所に関係なくFile1が壊れますusing。これは、コンパイラがusingディレクティブを調べる前に、最も内側にある名前空間を検索することを意味します。


28
これは、Markのmultiple-namespaces-in-one-file引数よりもusingステートメントをローカルに配置するほうがはるかに良い理由です。特に、コンパイル缶にサインを付けると、名前の衝突について不満を抱きます(このルールについては、StyleCopのドキュメントを参照してください(例:Jaredによって投稿されたもの))。
デビッドシュミット

148
受け入れられた答えは良いですが、私にとっては、using句を名前空間のに置くのがもっともな理由のようです。名前空間Outer.Innerにいる場合、System.Mathではなく、Outer.InnerのMathクラスを使用することを期待します。
フランクウォリス

7
私もこれに同意します。受け入れられた答えは、技術的に違いを説明しているという点で正しいです。ただし、どちらか一方のクラスには明示的なコールアウトが必要です。私は「Math」を自分のローカルクラスに解決させ、「System.Math」が外部クラスを参照することを非常に高く評価しています。たとえOuter.Mathが存在する前にSystem.Mathが「Math」として使用されていたとしてもです。はい、既存の参照をいくつ修正してももっと手間がかかりますが、Outer.Mathに別の名前を付ける必要があるかもしれないというヒントになるかもしれません!
mbmcavoy

13
素晴らしい答えですが、フレームワークを使用しないステートメントをローカルに配置し、フレームワークを使用するステートメントをグローバルに維持したいようです。私の好みを完全に変更する必要がある理由を誰かがさらに説明していますか?また、これはどこから来たのですか、VS2008のテンプレートは名前空間の外で使用していますか?
チミン

31
これは、使用する場所を変更するというよりは、悪い命名規則のほうが多いと思います。あなたの溶液中の数学というクラスがあってはならない
JDeveloperの

454

このスレッドにはすでにすばらしい回答がいくつかありますが、この追加の回答でもう少し詳しく説明できると思います。

まず、次のようにピリオドを含む名前空間宣言を思い出してください。

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

完全に次と同等です:

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

必要にusing応じて、これらすべてのレベルにディレクティブを配置できます。(もちろん、using1か所だけにsを置きたいのですが、言語によっては合法です。)

暗黙のタイプを解決するためのルールは、次のように大まかに説明できます。最初に一致の最も内側の「スコープ」を検索し、そこに何も見つからない場合は、1つのレベルから次のスコープに進み、そこで検索します。一致が見つかるまで。あるレベルで複数の一致が見つかった場合、タイプの1つが現在のアセンブリのものである場合、そのタイプを選択してコンパイラ警告を発行します。それ以外の場合は、あきらめてください(コンパイル時エラー)。

ここで、2つの主要な規則を使用した具体例で、これが何を意味するかを明確にしましょう。

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

上記の場合、タイプAmbiguousが何かを見つけるために、検索は次の順序で行われます。

  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

その他の規約:

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

次に、タイプの検索はAmbiguous次の順序で行われます。

  1. 内部のネストされたタイプC(継承されたネストされたタイプを含む)
  2. 現在の名前空間のタイプ MyCorp.TheProduct.SomeModule.Utilities
  3. タイプでSystemSystem.Collections.GenericSystem.LinqMyCorp.TheProductMyCorp.TheProduct.OtherModuleMyCorp.TheProduct.OtherModule.Integration、およびThirdParty
  4. 名前空間のタイプ MyCorp.TheProduct.SomeModule
  5. タイプ MyCorp
  6. でタイプヌル名前空間(グローバル名前空間)

(これMyCorp.TheProductは「3.」の一部だったので、「4。」と「5.」の間では不要でした。)

おわりに

名前空間宣言の内部または外部のどちらに使用しても、優先度の高い名前空間の1つに、後で誰かが同じ名前の新しい型を追加する可能性が常にあります。

また、ネストされた名前空間がタイプと同じ名前を持つ場合、問題が発生する可能性があります。

検索階層が変更され、別のタイプが見つかる可能性があるため、ある場所から別の場所に使用を移動することは常に危険です。したがって、慣習を1つ選択してそれを守ることで、使用法を変える必要がなくなります。

Visual Studioのテンプレートは、デフォルトでは、名前空間の外側に使用を配置します(たとえば、VSに新しいファイルで新しいクラスを生成させる場合)。

外部で使用することの(小さな)利点の1つは、グローバル属性のusingディレクティブを、たとえばの[assembly: ComVisible(false)]代わりに利用できることです[assembly: System.Runtime.InteropServices.ComVisible(false)]


46
これは、「using」ステートメントの位置が開発者の意図的な決定であるという事実を強調しているため、最良の説明です。影響を理解せずに、誰かが不注意に「using」ステートメントの場所を変更してはなりません。したがって、StyleCopルールは単にばかげています。
ZunTzu 2016年

194

名前空間の内部に置くと、宣言はファイルのその名前空間にローカルになります(ファイルに複数の名前空間がある場合)。ただし、ファイルごとに1つの名前空間しかない場合は、外部にあるかどうかに関係なく、大きな違いはありません。名前空間の内部。

using ThisNamespace.IsImported.InAllNamespaces.Here;

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

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

namespace Namespace3
{ 
   using ThisNamespace.IsImported.InJustNamespace3;
}

名前空間は、物理的な(ファイル)ではなく論理的な分離を提供します。
Jowen 2013

9
違いがないというのはよくありません。ブロックusing内のディレクティブnamespaceは、囲んでいるnamespaceブロックに基づいて相対名前空間を参照できます。
またはMapper

70
そうだね。私たちはこの質問の5年前に受け入れられた回答でそれを確立しました。
Mark Cidade、2014

59

よるとHanselman氏-指令および集合体Loading ...使用し、他のこのような製品は、技術的に違いはありません。

私の好みは、名前空間の外に置くことです。


3
@Chris M:ええと...回答に投稿されたリンクは、インとアウトのメリットがないことを示しており、実際に、投稿したリンクで行われた申し立てを偽る例を示しています...
ジョニー

2
はい、私はスレッドを完全には読んでいませんでしたが、MVPが正しいと言ったときに買いました。ある人はそれを反証し、彼のコードをさらに下に示しています... "C#コンパイラが生成するILはどちらの場合も同じです。実際、C#コンパイラは各usingディレクティブに対応するものをまったく生成しません。usingディレクティブは純粋にC#ism、それらは.NET自体には意味がありません(ステートメントの使用には当てはまりませんが、それらはまったく異なるものです)。groups.google.com/group/wpf
Chris McKee

84
リンクの概要を含めてください。リンクが壊れた場合(十分な時間が与えられる発生するため)、突然32の賛成票を持つ回答は価値がありMy style is to put them outside the namespaces.ます。
ANeves、2012年

11
ここの主張は単に間違っています...技術的な違いがあり、あなた自身の引用はそう言っています...実際、それがすべてです。この誤った答えを削除してください...はるかに優れた正確なものがあります。
ジムバルター2016

53

StyleCopドキュメントによると:

SA1200:UsingDirectivesMustBePlacedWithinNamespace

原因AC#usingディレクティブが名前空間要素の外に配置されています。

規則の説明ファイルに名前空間要素が含まれていない場合を除き、usingディレクティブまたはusing-aliasディレクティブが名前空間要素の外部に配置されると、この規則の違反が発生します。

たとえば、次のコードでは、このルールの2つの違反が発生します。

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'と競合する定義が含まれています

このコードは、Guidと呼ばれるSystem.Guid型のエイリアスを作成し、対応するコンストラクターインターフェイスを使用して、Guidと呼ばれる独自の型も作成します。その後、コードはタイプGuidのインスタンスを作成します。このインスタンスを作成するには、コンパイラは2つの異なるGuid定義から選択する必要があります。using-aliasディレクティブが名前空間要素の外にある場合、コンパイラーはローカル名前空間内で定義されたGuidのローカル定義を選択し、名前空間外で定義されたusing-aliasディレクティブを完全に無視します。残念ながら、これはコードを読むときには明らかではありません。

ただし、using-aliasディレクティブが名前空間内に配置されている場合、コンパイラーは、同じ名前空間内で定義されている、競合する2つの異なるGuidタイプから選択する必要があります。これらのタイプは両方とも、一致するコンストラクターを提供します。コンパイラは決定を下すことができないため、コンパイラエラーにフラグを立てます。

名前空間の外にusing-aliasディレクティブを配置することは、このような状況で混乱を招く可能性があるため、実際にはどのバージョンの型が使用されているかが明確ではないため、不適切です。これにより、診断が困難なバグが発生する可能性があります。

名前空間要素内にusing-aliasディレクティブを配置すると、バグの原因としてこれを排除できます。

  1. 複数の名前空間

単一のファイル内に複数の名前空間要素を配置することは一般に悪い考えですが、これを行う場合、すべてのusingディレクティブをファイルの上部ではなく、各名前空間要素内に配置することをお勧めします。これにより、名前空間のスコープが厳しくなり、上記のような動作を回避するのにも役立ちます。

名前空間の外に配置されたディレクティブを使用してコードが記述されている場合、これらのディレクティブを名前空間内で移動するときは、コードのセマンティクスが変更されないように注意する必要があります。上記で説明したように、using-aliasディレクティブを名前空間要素内に配置すると、ディレクティブが名前空間の外部に配置された場合に発生しない方法で、コンパイラーが競合するタイプを選択できます。

違反の修正方法このルールの違反を修正するには、すべてのusingディレクティブとusing-aliasディレクティブを名前空間要素内に移動します。


1
@Jared-私が私の回答で述べたように、私の好ましい回避策/解決策は、ファイルごとに1つのクラスのみを持つことです。これはかなり一般的な慣習だと思います。
benPearce 2009

24
実際、これはStyleCopルールでもあります。SA1402:すべてのクラスが部分的で同じタイプでない限り、AC#ドキュメントにはルートレベルで単一のクラスのみが含まれます。別のルールを破ることによって1つのルールを紹介し、間違ったソースでたたきます。
タスク

6
StyleCopの観点から実際にカバーする最初の回答であることに賛成。個人的にusingは、名前空間外のsの視覚的な感覚が好きです。インナーusingsは私にはとても醜く見えます。:)
nawfal

2
最後に質問への良い答え。そして、benPearceのコメントは無関係です...これはファイル内のクラスの数とは関係ありません。
ジムバルター2016

35

エイリアスを使用する場合、ネームスペース内に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>>>;

きれいじゃない。


1
あなたのclassニーズ名前(識別子)。指定したusingとおり、クラス内にディレクティブを含めることはできません。名前空間レベルにある必要があります。たとえば、最外部の外側namespace、または最内部のすぐ内側namespace(ただし、クラス/インターフェースなどの内部ではありません)。
Jeppe Stig Nielsen

@JeppeStigNielsenありがとう。usingディレクティブを誤って置き間違えました。意図したとおりに編集しました。ご指摘ありがとうございます。ただし、推論は同じです。
ネオ

4

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

8
「外部の名前空間を省略できる」ことは事実ですが、そうする必要はありません。私にとって、これは、ディレクティブ(@Neoの回答のようなエイリアス以外)を使用して名前空間の外に出て、完全修飾の名前空間名を強制する必要がある理由に関する別の議論です。
キースロバートソン

4

私が遭遇した1つのしわ(それは他の回答ではカバーされていません):

次の名前空間があるとします。

  • 何か。その他
  • 親。何か。その他

using Something.Other で使用する場合はnamespace Parent、最初の(Something.Other)を指します。

ただし、その名前空間宣言で使用すると、2番目の名前空間(Parent.Something.Other)が参照されます。

簡単な解決策がありglobal::ます:" "プレフィックスを追加します:docs

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

2

技術的な理由は回答で議論されており、違いはそれほど大きくなく、両方のトレードオフがあるため、結局のところ個人的な好みになると思います。.csファイルを作成するためのVisual Studioのデフォルトテンプレートはusing、名前空間外のディレクティブを使用します。

次のようにプロジェクトファイルのルートにファイルusingを追加することで、名前空間外のディレクティブをチェックするようにスタイルコップを調整できstylecop.jsonます。

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

この構成ファイルをソリューションレベルで作成し、「既存のリンクファイル」としてプロジェクトに追加して、すべてのプロジェクトで構成を共有することもできます。


2

他の回答でカバーされていないと私が信じていないもう1つの微妙な点は、同じ名前のクラスと名前空間がある場合です。

名前空間内にインポートがあると、クラスが見つかります。インポートがネームスペースの外にある場合、インポートは無視され、クラスとネームスペースは完全修飾する必要があります。

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

-8

ソースソリューションで使用される「参照」を使用するデフォルトが名前空間の外にあり、「新しく追加された参照」であるものを名前空間内に置くことをお勧めします。これは、追加されている参照を区別するためです。


6
いいえ、実際にはそれは悪い考えです。ディレクティブが新たに追加されたかどうかに基づいて、ローカルスコープとグローバルスコープの使用ディレクティブ間の場所を基にしないでください。代わりに、BCL参照を除いて、アルファベット順に並べることをお勧めします。
Abel
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.