C#で「using」ディレクティブを使用しないのはなぜですか?


71

大規模なC#プロジェクトの既存のコーディング標準には、すべての型名を完全修飾するというルールが含まれており、「using」ディレクティブの使用を禁止しています。だから、おなじみではなく:

using System.Collections.Generic;

.... other stuff ....

List<string> myList = new List<string>();

var禁止されていることはおそらく驚くことではありません。)

私は最終的に:

System.Collections.Generic.List<string> myList = new System.Collections.Generic.List<string>();

それはタイピングの134%の増加であり、その増加のどれも有用な情報を提供しません。私の見解では、増加の100%は実際に理解を妨げるノイズ(混乱)です。

30年以上のプログラミングの中で、私はそのような標準が一度か二度提案されたのを見たことがありますが、実装されたことはありません。その背後にある理論的根拠は私を逃れます。標準を課している人は愚かではなく、私は彼が悪意があるとは思わない。私が何かを逃していない限り、それは他の唯一の選択肢として見当違いのままになります。

そのような標準が課されていることを聞いたことがありますか?もしそうなら、その背後にある理由は何でしたか?usingこの禁止を取り除くようにこの人に説得するかもしれない「それは愚かだ」または「他の誰もが採用している」以外の議論を考えることができますか?

推論

この禁止の理由は次のとおりです。

  1. 完全に修飾された型を取得するには、名前の上にマウスを移動するのは面倒です。常に完全修飾型を常に表示することをお勧めします。
  2. 電子メールで送信されたコードスニペットには完全修飾名がないため、理解が難しい場合があります。
  3. Visual Studioの外部(Notepad ++など)でコードを表示または編集する場合、完全修飾型名を取得することはできません。

私の主張は、3つすべてのケースがまれであり、少数のまれなケースに対応するためだけに、誰もが混乱して理解しにくいコードの代価を支払うようにすることは見当違いだということです。

潜在的な名前空間の競合の問題は、私が主な関心事であると予想していましたが、言及さえされていませんでした。名前空間があるためMyCompany.MyProject.Core、これは特に驚くべきことです。これは特に悪い考えです。何かを命名しSystemたりCore、C#で命名することは、狂気への素早い道であることをずっと前に知りました。

他の人が指摘したように、名前空間の競合は、リファクタリング、名前空間のエイリアス、または部分的な修飾によって簡単に処理されます。


コメントは詳細なディスカッション用ではありません。この会話はチャットに移動さました
maple_shaft

1
なぜそれが良いアイデアであるかの実例(しかしC ++の世界では):なぜ「名前空間stdを使用する」が悪い習慣と考えられるのかに対する答え、「「使用」ディレクティブと宣言の両方が禁止されている」近く。
ピーターモーテンセン

推論のセクションを読む前に、私の会社には同様のルールがあるため、非現実的な理由を推測しました。
Gqqnbig

回答:


69

より広範な質問:

そのような標準が課されていることを聞いたことがありますか?もしそうなら、その背後にある理由は何でしたか?

はい、私はこれを聞いたことがあり、完全修飾オブジェクト名を使用すると、名前の衝突を防ぎます。まれではありますが、発生した場合、それらを把握するのは非常に厄介です。


例: このタイプのシナリオは、おそらく例を使用してより適切に説明されます。

我々は2持っていると言うLists<T>二つの別々のプロジェクトに属します。

System.Collections.Generic.List<T>
MyCorp.CustomCollections.Optimized.List<T>

完全修飾オブジェクト名を使用すると、どちらList<T>が使用されているかが明確になります。その明瞭さは明らかに冗長性を犠牲にしてもたらされます。

そして、あなたは「待って!誰もそのようなリストを2つ使用することはないだろう!」と主張するかもしれません。ここでメンテナンスシナリオを指摘します。

Foo承認され最適化された企業を使用する企業向けのモジュールを作成しましたList<T>

using MyCorp.CustomCollections.Optimized;

public class Foo {
    List<object> myList = ...;
}

後で、新しい開発者があなたがやってきた仕事を延長することにしました。会社の基準を知らずに、usingブロックを更新します。

using MyCorp.CustomCollections.Optimized;
using System.Collections.Generic;

そして、急いで物事がどのように悪くなるかを見ることができます。

同じプロジェクト内の異なる名前空間にある同じ名前の2つの独自クラスを持つことができることを指摘するのは簡単なはずです。そのため、.NETフレームワークが提供するクラスとの衝突に関する懸念だけではありません。

MyCorp.WeightsAndLengths.Measurement();
MyCorp.TimeAndSpace.Measurement();

現実:
さて、これはほとんどのプロジェクトで起こりそうですか?いいえ、そうでもありません。しかし、多くの入力がある大規模なプロジェクトで作業しているときは、物事が爆発する可能性を最小限に抑えるために最善を尽くします。

複数の貢献チームを持つ大規模プロジェクトは、アプリケーションの世界では特別な種類の獣です。プロジェクトへの入力ストリームと、貢献者がプロジェクトのガイドラインを読んでいない可能性があるため、他のプロジェクトにとって不合理と思われるルールがより適切になります。

これは、2つの大きなプロジェクトが一緒にマージされるときにも発生する可能性があります。両方のプロジェクトに同じ名前のクラスがある場合、1つのプロジェクトから別のプロジェクトへの参照を開始すると衝突が発生します。また、プロジェクトが大きすぎてリファクタリングできない場合や、経営陣がリファクタリングに費やした時間を賄うための費用を承認しない場合もあります。


代替案:
質問はしませんでしたが、これは問題の優れた解決策でないことを指摘する価値があります。名前空間宣言なしで衝突するクラスを作成するのは得策ではありません。

List<T>、特に、予約語として扱われ、クラスの名前として使用されるべきではありません。

同様に、プロジェクト内の個々の名前空間は、一意のクラス名を持つよう努める必要があります。どのネームスペースを使用しているのFoo()かを思い出して思い出す必要があるのは、精神的なオーバーヘッドです。持つ:別の方法が言ったMyCorp.Bar.Foo()MyCorp.Baz.Foo()、あなたの開発者まで、最高の回避をトリップする予定です。

それ以外の場合は、あいまいさを解決するために部分的な名前空間を使用できます。たとえば、どちらのFoo()クラスも絶対に名前を変更できない場合は、部分的な名前空間を使用できます。

Bar.Foo() 
Baz.Foo()

現在のプロジェクトの具体的な理由:

その基準に従って現在のプロジェクトに与えられた特定の理由で質問を更新しました。それらを見て、バニートレイルを本当に脱線しましょう。

完全に修飾された型を取得するには、名前の上にマウスを移動するのは面倒です。常に完全修飾型を常に表示することをお勧めします。

「面倒?」いいえ。おそらく迷惑です。画面上のポインターを移動するために数オンスのプラスチックを移動するのは面倒ではありません。しかし、私は脱線します。

この推論の行は、他の何よりも隠蔽のように見えます。ちなみに、アプリケーション内のクラスの名前は弱く、クラス名を取り巻く適切な量のセマンティック情報を収集するには、名前空間に依存する必要があると思います。

これは完全修飾クラス名の有効な正当化ではありません。おそらく、部分修飾クラス名を使用する正当な正当化です。

電子メールで送信されたコードスニペットには完全修飾名がないため、理解が難しい場合があります。

この(続き?)の推論の行は、クラスの名前が現在不十分であるという私の疑念を補強します。繰り返しますが、貧弱なクラス名を持っていることは、すべてが完全修飾クラス名を使用することを要求するための良い正当化ではありません。クラス名を理解するのが難しい場合、完全修飾クラス名で修正できるものよりも多くの間違いがあります。

Visual Studioの外部(Notepad ++など)でコードを表示または編集する場合、完全修飾型名を取得することはできません。

すべての理由のうち、これは私に飲み物を吐き出させた。しかし、再び、私は脱線します。

チームがVisual Studioの外部でコードを頻繁に編集または表示しているのなぜだろうと思います。そして今、私たちは、名前空間が提供するものとかなり直交する正当性を検討しています。これはツールに裏打ちされた引数ですが、名前空間はコードに組織構造を提供するためにあります。

あなた自身のプロジェクトは、ツールが彼らに提供できるものを利用していない開発者とともに、貧弱な命名規則に苦しんでいるように思えます。そして、それらの実際の問題を解決するのではなく、症状の1つを介してバンドエイドを平手打ちしようとし、完全修飾クラス名を要求しています。これを誤ったアプローチとして分類しても安全だと思います。

適切に名前が付けられていないクラスがあり、リファクタリングできないと仮定した場合、正しい答えはVisual Studio IDEを最大限に活用することです。VS PowerToolsパッケージのようなプラグインの追加を検討してください。次に、AtrociouslyNamedClass()クラス名をクリックしてF12を押し、クラスの定義に直接移動して、何をしようとしているのかをよりよく理解することができます。同様に、Shift-F12をクリックして、現在使用する必要があるコード内のすべてのスポットを見つけることができますAtrociouslyNamedClass()

外部のツーリングの懸念に関して-行うための最善のことは、それを止めることです。参照先がすぐにわからない場合は、スニペットをメールでやり取りしないでください。Visual Studio以外のツールには、チームが必要とするコードを取り巻くインテリジェンスがないため、使用しないでください。Notepad ++は素晴らしいツールですが、このタスクには適していません。

ですから、あなたが提示された3つの特定の正当化に関するあなたの評価に同意します。そうは言っても、「このプロジェクトには対処できない/対処できない根本的な問題があり、これが私たちがそれらを「修正」する方法です」と言われたと思います。そして、それは明らかに、チーム内のより深刻な問題を物語っています。


1
+1。また、実際に内部名前空間でいくつかの厄介な衝突に遭遇しました。レガシーコードを「2.0」プロジェクトに移植する場合と、異なる名前空間コンテキストで意味的に有効ないくつかのクラス名を選択する場合の両方。本当に厄介なのは、同じ名前の2つのものによく似たインターフェースがあるため、コンパイラーが文句を言わないということです...ランタイムはそうなります!
svidgen

23
この問題は、明示的な名前空間の使用で混乱することによりコードを必要とする愚かなルールを課すことではなく、名前空間エイリアスを使用することで解決されます。
デビッドアルノ

4
@DavidArno-私はあなたに同意しません。ただし、大規模なプロジェクトにはそのオプションがありません。大規模なプロジェクトがその規則を課す理由を指摘しているだけです。しかし、私は規則を支持していません。チームには他のオプションがないため、それが必要になることがあります。

4
@ GlenH7あなたはあなたの答えで他の利用可能なソリューションを指定することができます。私は人々がこれに出くわして「ああ、それは素晴らしいアイデアだ」と思うのを嫌います。ただし、残りの客観的かつ実用的な場合は+1。
ラバーダック

3
@JimMischel-ルールが何かの松葉杖として使用されているようです。それが何であるかを決定することは不可能かもしれません。高いレベルでは、はい、許可しないことの正当化がある場合がありusingsますが、プロジェクトがそれらのケースの1つとして適格であるとは思えません。

16

同じ名前でクラスライブラリが異なる多くのクラスがある会社で働いていました。

例えば、

  • Domain.Customer
  • Legacy.Customer
  • ServiceX.Customer
  • ViewModel.Customer
  • Database.Customer

悪いデザインと言うかもしれませんが、これらの事柄が有機的にどのように展開するかを知っていますし、代替案は何ですか?名前空間を含めるためにすべてのクラスの名前を変更しますか?すべての主要なリファクタリング?

いずれにせよ、これらすべての異なるライブラリを参照するプロジェクトがクラスの複数のバージョンを使用する必要がある場所がいくつかありました。

using直接上部に、これは、お尻の主要な痛みだったのReSharperは奇妙な事が書き換え、試してみて、それを動作させるために行うだろうusingオンザフライディレクティブは、あなたが顧客can't被キャスト-れたままになるだろう顧客スタイルのエラーであり、どのタイプが正しいタイプであるかがわかりません。

そのため、少なくとも部分的な名前空間があれば、

var cust = new Domain.Customer

または

public void Purchase(ViewModel.Customer customer)

コードの可読性が大幅に向上し、必要なオブジェクトが明示されました。

これはコーディング標準ではなく、完全な名前空間ではなく、標準としてすべてのクラスに適用されませんでした。


13
「代わりに、名前空間を含むようにすべてのクラスの名前を変更しますか?すべてを大幅にリファクタリングしますか?」はい、それは(正しい)代替案です。
デビッドアルノ

2
短期的にのみ。長期的にコードで明示的な名前空間を使用するよりもはるかに安価です。
デビッドアルノ

3
あなたが株式ではなく現金で私に支払っている限り、その議論を受け入れてうれしい。
ユアン

8
@David:いいえ、それは正しい選択肢ではありません。すべての識別子または少なくとも実際に衝突する識別子のいずれかを完全に修飾することが正しい方法であり、そもそも名前空間が存在する理由は、異なるモジュールで同じ名前が使用されたときに衝突を提供するためです。重要なのは、一部のモジュールはサードパーティのものである可能性があり、コードベースは変更できないことです。2つの異なるサードパーティライブラリの識別子が衝突し、両方のライブラリを使用する必要があるが、どちらもリファクタリングできない場合、どうしますか?リファクタリングが常に可能であるとは限らないため、名前空間が存在します。
カイゼルディ

4
@CarlSixsmith、リファクタリングに関するMartin Fowlerの見解を購読している場合、その変更がユーザーに影響を与えるなら、あなたは正しい、それはリファクタリングではありません。それは別の形態の再構築です。ただし、これには2つのポイントがあります。1。すべてのパブリックメソッドは自動的にAPIの一部ですか?2. ファウラー自身は、この定義をめぐって負けている戦いしていることを認めており、「リファクタリング」という用語を使用して、公共の変化を含む一般的なリストラを意味する人々が増えています。
デビッドアルノ

7

冗長すぎたり短すぎたりする良いことはありません。コードを書きすぎないようにしながら、微妙なバグを防ぐために、十分に詳細に説明するのに黄金の中間点を見つける必要があります。

C#では、その例は引数の名前です。解決策を探すのに何時間も費やした問題何度か遭遇しましたが、より冗長なスタイルで書くと、そもそもバグを防ぐことができました。問題は、引数の順序のエラーで構成されています。たとえば、あなたにぴったりでしょうか?

if (...) throw new ArgumentNullException("name", "The name should be specified.");
if (...) throw new ArgumentException("name", "The name contains forbidden characters.");

一見、見た目はまったく問題ありませんが、バグがあります。例外のコンストラクタのシグネチャは次のとおりです。

public ArgumentException(string message, string paramName)
public ArgumentNullException(string paramName, string message)

引数の順序に気付きましたか?

メソッドが同じタイプの2つ以上の引数を受け取るたびに引数の名前が指定される、より冗長なスタイルを採用することにより、エラーが再び発生するのを防ぎます。

if (...) throw new ArgumentNullException(paramName: "name", message: "The name should be specified.");
if (...) throw new ArgumentException(paramName: "name", message: "The name contains forbidden characters.");

書くのは明らかに長くなりますが、無駄なデバッグが少なくなります。

極端にプッシュすると、パラメーターの順序を混在させる方法がないようなメソッドなど、あらゆる場所で名前付き引数の使用を強制できdoSomething(int, string)ます。これはおそらくプロジェクトを長期的に害し、上で説明したバグを防ぐという利点がなくても、はるかに多くのコードが作成されます。

あなたの場合、「いいえusing」ルールの背後にある動機は、MyCompany.Something.List<T>vs System.Collections.Generic.List<T>. などの間違ったタイプを使用することをより難しくする必要があるということです。

個人的に、私はそのようなルールを避けるでしょう、なぜなら私見では、読みにくいコードのコストは間違ったタイプを誤用するリスクをわずかに減らす利点をはるかに上回っていますが、少なくともこのルールには正当な理由があります。


このような状況はツールによって検出できます。ReSharper paramNameInvokerParameterName属性でをマークし、そのようなパラメーターが存在しない場合は警告を発行します。
ジョンボット

2
@Johnbot:確かにできますが、非常に限られたケースです。私が持っている場合はどうなりSomeBusiness.Something.DoStuff(string name, string description)ますか?何が名前で何が説明であるかを理解できるほど賢いツールはありません。
Arseni Mourzenko

この場合の@ArseniMourzenkoは、呼び出しが正しいかどうかを判断するために人間でさえ時間をかける必要があります。
Gqqnbig

6

名前空間はコードに階層構造を与え、短い名前を可能にします。

   MyCompany.Headquarters.Team.Leader
   MyCompany.Warehouse.Team.Leader

「using」キーワードを許可すると、一連のイベントがあります...

  • 誰もが1つのグローバル名前空間を実際に使用しています
    (必要なすべての名前空間が含まれているため)
  • 人々が名前の衝突を起こし始める、または
  • 名前の衝突を心配する

したがって、リスクを回避するために、誰もが本当に長い名前を使用し始めます。

   HeadquartersTeamLeader
   WarehouseTeamLeader

状況はエスカレートします...

  • 他の人がすでに名前を選択している可能性があるため、長い名前を使用します。
  • 名前はますま​​す長くなります。
  • 長さは最大で60文字を超えることができます-それはばかげています。

私はこれが起こるのを見たので、「使用」を禁止する企業に同情することができます。


11
禁止usingは核の選択肢です。名前空間のエイリアスと部分修飾が望ましいです。
ジムミッシェル

1

問題usingは、明示的な宣言なしで名前空間に魔法のように追加することです。これはList<>、ドメインに精通しておらず、どのusing句がそれを導入したかを知らないような何かに遭遇したメンテナンスプログラマーにとってははるかに大きな問題です。

Javaでも、たとえば書き込みimport Java.util.*;ではなく同様の問題が発生しますimport Java.util.List;。後者の宣言は、どの名前が導入されたかを文書化するためList<>、ソースの後半で発生した場合、その起源はimport宣言からわかります。


6
ドメインに不慣れな人が多少の困難を感じるのは事実ですが、彼がしなければならないのは、タイプ名の上にマウスを移動するだけで、完全修飾名を取得します。または、右クリックして直接タイプ定義に移動できます。.NET Framework型の場合、彼はおそらく物事がどこにあるか既に知っているでしょう。ドメイン固有のタイプの場合、快適になるにはおそらく1週間かかります。その時点で、完全修飾名は理解を妨げる単なるノイズです。
ジムミッシェル

Jim ..マウスを定義の上に置いてみたところ、うまくいきませんでした。世界中のプログラマーがMicrosoft IDEを使用しない可能性を考慮しましたか?いずれにせよ、あなたのカウンターはとソースという、私のポイントは無効になりません使用しては、もはや自己文書がある
マイケル・モンテネーロ

2
はい、C#で働いており、利用可能な最高のツールを使用していないことで自分自身に障害を持っている人がいます。そして、完全な資格は「自己文書化」であるという議論にはいくつかのメリットがありますが、そのようなコードは読みやすさに多大なコストがかかります。無関係なコードはすべて、本当に重要なコードの部分をあいまいにするノイズを作成します。
ジムミッシェル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.