私はいくつかの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
ディレクティブを調べる前に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
にはすでに定義されているので、System
はユーザー定義クラスにとっては不適切な名前である可能性があると主張する人もいます。ここで重要な点は、 が の違いであり、コードの保守性に影響するということです。
Foo
がOuter.Inner
ではなく名前空間Outer
にある場合に何が起こるかに注意することも興味深いです。その場合、File2にOuter.Math
を追加すると、using
の場所に関係なくFile1が壊れます。これは、using
ディレクティブを調べる前に、コンパイラーが最も内側の名前空間を検索することを意味します。
このスレッドには既にいくつかの素晴らしい回答がありますが、この追加の回答でもう少し詳しく説明できると思います。
まず、次のようなピリオドを含む名前空間宣言を覚えておいてください。
namespace MyCorp.TheProduct.SomeModule.Utilities
{
...
}
以下と完全に同等です:
namespace MyCorp
{
namespace TheProduct
{
namespace SomeModule
{
namespace Utilities
{
...
}
}
}
}
必要に応じて、これらのすべてのレベルにusing
ディレクティブを配置できます。 (もちろん、1つの場所にusing
sを配置したいだけですが、言語によっては合法です。)
暗示されるタイプを解決するためのルールは、このように大まかに述べることができます:最初に一致する最も内側の「スコープ」を検索します。など、一致が見つかるまで。あるレベルで複数の一致が見つかった場合、タイプの1つが現在のアセンブリからのものである場合、その1つを選択し、コンパイラ警告を発行します。それ以外の場合は、giveめます(コンパイル時エラー)。
次に、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
のタイプを調べるために、検索は次の順序で行われます。
C
内のネストされたタイプ(継承されたネストされたタイプを含む)MyCorp.TheProduct.SomeModule.Utilities
MyCorp.TheProduct.SomeModule
MyCorp.TheProduct
のタイプMyCorp
のタイプSystem
、System.Collections.Generic
、System.Linq
、MyCorp.TheProduct.OtherModule
、MyCorp.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
の検索を次の順序で行います。
C
内のネストされたタイプ(継承されたネストされたタイプを含む)MyCorp.TheProduct.SomeModule.Utilities
System
、System.Collections.Generic
、System.Linq
、MyCorp.TheProduct
、MyCorp.TheProduct.OtherModule
、MyCorp.TheProduct.OtherModule.Integration
、およびThirdParty
のタイプMyCorp.TheProduct.SomeModule
MyCorp
のタイプ(MyCorp.TheProduct
は「3.」の一部であったため、「4。」と「5.」の間では不要であったことに注意してください。)
結語
名前空間宣言の内部または外部のどちらに使用を配置しても、誰かが後で優先順位の高い名前空間の1つに同じ名前の新しい型を追加する可能性が常にあります。
また、ネストされた名前空間が型と同じ名前を持つ場合、問題が発生する可能性があります。
検索階層が変更され、別のタイプが見つかる可能性があるため、ある場所から別の場所に使用を移動することは常に危険です。したがって、1つの規則を選択し、それに固執することで、これを使用して移動する必要がなくなります。
Visual Studioのテンプレートは、既定で、名前空間の使用outsideを配置します(たとえば、VSで新しいファイルに新しいクラスを生成する場合)。
Usingoutsideを使用する1つの(小さな)利点は、[Assembly: ComVisible(false)]
の代わりに[Assembly: System.Runtime.InteropServices.ComVisible(false)]
のように、グローバル属性のusingディレクティブを利用できることです。
名前空間の内側に配置すると、宣言はファイルのその名前空間に対してローカルになります(ファイル内に複数の名前空間がある場合)。ただし、名前空間がファイルごとに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;
}
Hanselmanによると - 指令とアセンブリのロードを使用して... および他のそのような記事は技術的に違いはありません。
私の好みはそれらを名前空間の外に置くことです。
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");
を含む行にある以下のコンパイラエラーで失敗します。
CS0576:ネームスペース 'Microsoft.Sample'にエイリアス 'Guid'と競合する定義が含まれています
このコードは、GuidというSystem.Guid型のエイリアスを作成し、さらに対応するコンストラクタインターフェイスを持つGuidという独自の型も作成します。その後、コードはGuid型のインスタンスを作成します。このインスタンスを作成するには、コンパイラは2つの異なるGuidの定義から選択する必要があります。 using-aliasディレクティブをnamespace要素の外側に置くと、コンパイラはローカルネームスペース内で定義されているGuidのローカル定義を選択し、ネームスペースの外側で定義されているusing-aliasディレクティブを完全に無視します。残念ながら、これはコードを読むときには明らかではありません。
しかし、using-aliasディレクティブがネームスペース内に配置されている場合、コンパイラは、同じネームスペース内で定義された2つの異なる競合するGuid型の中から選択する必要があります。これらの型はどちらも一致するコンストラクタを提供します。コンパイラは決定を下すことができないため、コンパイラエラーのフラグが立てられます。
Using-aliasディレクティブをネームスペースの外側に配置することは、実際に使用されている型のバージョンが明らかでない場合など、混乱を招く可能性があるため、不適切な方法です。これは潜在的に診断が難しいかもしれないバグを引き起こす可能性があります。
Using-aliasディレクティブをnamespace要素内に配置することで、これをバグの原因として排除できます。
1つのファイル内に複数の名前空間要素を配置するのは一般的に悪い考えですが、これを行う場合は、すべてのusingディレクティブをファイルの先頭に配置するのではなく、各名前空間要素内に配置することをお勧めします。これは名前空間を厳密に範囲指定し、上記のような振る舞いを回避するのにも役立ちます。
コードが名前空間の外側に置かれたディレクティブを使って書かれているとき、これらのディレクティブを名前空間の中で動かすとき、これがコードの意味論を変えないことを確実にするために注意が払われるべきです。上で説明したように、namespace要素内にusing-aliasディレクティブを配置すると、コンパイラーは、ディレクティブが名前空間の外側に配置されていても起こらない方法で、矛盾する型を選択できます。
違反を修正する方法この規則の違反を修正するには、namespace要素内でusingディレクティブとusing-aliasディレクティブをすべて移動します。
エイリアスを使用したいときに、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>>>;
かわいくないです。
Jeppe Stig Nielsen 言われた として、このスレッドはすでにすばらしい答えを持っています、しかし私はこのかなり明白な微妙さもまた言及する価値があると思いました。
名前空間の内側で指定されたusing
ディレクティブは、外側で指定されたときのように完全修飾される必要がないため、コードを短くすることができます。
次の例は、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;
}
}
他の答えではカバーされていないと思われるもう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.
}
}
}
私が遭遇した1つのしわ(それは他の答えでカバーされない):
これらの名前空間があるとします。
using Something.Other
のnamespace Parent
outside を使うとき、それは最初のもの(Something.Other)を参照します。
ただし、その名前空間宣言の inside を使用すると、2番目の宣言(Parent.Something.Other)が参照されます。
簡単な解決策があります: "global::
"接頭辞を追加してください: docs
namespace Parent
{
using global::Something.Other;
// etc
}
技術的な理由は答えの中で議論されています、そしてそれは違いが big ではなくそしてそれらの両方のためにトレードオフがあるので私はそれが最後に個人的な好みに来ると思います。 .cs
ファイルを作成するためのVisual Studioのデフォルトテンプレートは、名前空間の外側でusing
ディレクティブを使用します。
次のようにプロジェクトファイルのルートにstylecop.json
ファイルを追加することで、ネームスペースの外側でusing
ディレクティブをチェックするようにstylecopを調整できます。
{
"$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
"orderingRules": {
"usingDirectivesPlacement": "outsideNamespace"
}
}
}
この設定ファイルをソリューションレベルで作成し、「既存のリンクファイル」としてプロジェクトに追加して、すべてのプロジェクトで設定を共有することもできます。