一部の C# コードでStyleCopを実行していますが、using
ディレクティブが名前空間内にある必要があると報告され続けています。
using
名前空間の外側ではなく内側にディレクティブを配置する技術的な理由はありますか?
一部の C# コードでStyleCopを実行していますが、using
ディレクティブが名前空間内にある必要があると報告され続けています。
using
名前空間の外側ではなく内側にディレクティブを配置する技術的な理由はありますか?
実際には、この 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
これらのディレクティブを調べる前に検索するため、代わりに. 残念ながら (またはおそらく幸いなことに?)、にはメンバーがないため、File1 は壊れています。using
Outer.Math
System.Math
Outer.Math
PI
これはusing
、次のように名前空間宣言内に配置すると変更されます。
// File1b.cs
namespace Outer.Inner
{
using System;
class Foo
{
static void Bar()
{
double d = Math.PI;
}
}
}
これで、コンパイラは を検索するSystem
前に検索Outer
し、 を見つけSystem.Math
、すべて問題ありません。
Math
にすでにあるので、ユーザー定義クラスの悪い名前かもしれないと主張する人もいSystem
ます。ここでのポイントは、違いがあり、コードの保守性に影響するということです。
がではなくFoo
namespace にある場合に何が起こるかについても興味深い点です。その場合、File2 を追加すると、どこに行っても File1 が壊れます。これは、コンパイラがディレクティブを調べる前に、最も内側にある名前空間を検索することを意味します。Outer
Outer.Inner
Outer.Math
using
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 - Using Directive and Assembly Loading...および他のそのような記事によると、技術的に違いはありません。
私の好みは、それらを名前空間の外に置くことです。
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 型のインスタンスを作成します。このインスタンスを作成するには、コンパイラは Guid の 2 つの異なる定義から選択する必要があります。using-alias ディレクティブが名前空間要素の外に配置されると、コンパイラは、ローカル名前空間内で定義された Guid のローカル定義を選択し、名前空間の外で定義された using-alias ディレクティブを完全に無視します。残念ながら、これはコードを読んでも明らかではありません。
ただし、using-alias ディレクティブが名前空間内に配置されている場合、コンパイラは、同じ名前空間内で両方とも定義されている、競合する 2 つの異なる Guid 型から選択する必要があります。これらのタイプは両方とも、一致するコンストラクターを提供します。コンパイラは決定を下すことができないため、コンパイラ エラーにフラグを立てます。
using-alias ディレクティブを名前空間の外に配置することは、このような状況で混乱を招く可能性があるため、悪い習慣です。型のどのバージョンが実際に使用されているかが明らかではありません。これにより、診断が困難なバグが発生する可能性があります。
名前空間要素内に using-alias ディレクティブを配置すると、これがバグの原因となることがなくなります。
単一のファイル内に複数の名前空間要素を配置することは一般的には悪い考えですが、これを行う場合は、すべての using ディレクティブをファイルの先頭にグローバルに配置するのではなく、各名前空間要素内に配置することをお勧めします。これにより、名前空間のスコープが厳密になり、上記の種類の動作を回避するのにも役立ちます。
名前空間の外に配置された using ディレクティブを使用してコードが記述されている場合、これらのディレクティブを名前空間内に移動するときは、コードのセマンティクスが変更されないように注意する必要があります。上で説明したように、名前空間要素内に using-alias ディレクティブを配置すると、コンパイラは、ディレクティブが名前空間の外に配置された場合には発生しない方法で、競合する型から選択できます。
違反を修正する方法 この規則違反を修正するには、名前空間要素内のすべての 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;
}
}
ソース ソリューションで使用される"参照" を使用するデフォルトを名前空間の外に配置することをお勧めします。これは、どの参照が追加されているかを区別するためです。