web-dev-qa-db-ja.com

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

大規模な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年以上の間に、そのような標準が1回または2回提案されるのを見ましたが、実装されたことはありません。その背後にある理論的根拠は私を逃れます。標準を課す人は愚かではなく、私は彼が悪意があるとは思わない。私が何かを見逃していない限り、それは他の唯一の選択肢として見当違いを残します。

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

推論

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

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

私の主張は、3つすべてのケースはまれであり、少数のまれなケースに対応するためだけに、雑然としていて理解しにくいコードの代金を皆に払わせるのは誤った見方です。

名前空間の競合の潜在的な問題は、私が主な関心事であると予想していましたが、言及されていませんでした。名前空間MyCompany.MyProject.Coreがあるため、これは特に驚くべきことですが、これは特に悪い考えです。 C#でSystemまたはCoreに名前を付けることは、狂気へのクイックパスであることをずっと前に学びました。

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

77
Jim Mischel

より広い質問:

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

はい、これを聞いたことがあります。完全修飾オブジェクト名を使用すると、名前の衝突を防ぐことができます。まれではありますが、それらが発生した場合、それらを理解するのは非常に困難です。


例:このタイプのシナリオは、例を使用して説明したほうがよいでしょう。

2つの別々のプロジェクトに属する2つの_Lists<T>_があるとします。

_System.Collections.Generic.List<T>
MyCorp.CustomCollections.Optimized.List<T>
_

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

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

あなたは、承認され最適化された_List<T>_を使用する企業向けのモジュールFooを作成しました。

_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()
_

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

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

完全修飾タイプを取得するには、名前の上にマウスを置くのが面倒です。常に完全修飾型を常に表示しておくことをお勧めします。

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

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

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

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

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

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

すべての理由の中で、これは私に私の飲み物を吐き出させました。しかし、再び、私は余談です。

なぜチームはVisual Studioの外でコードを頻繁に編集または表示しているのでしょうか?そして今、私たちは名前空間が提供することを意図しているものにかなりよく直交する正当化を調べています。これはツールに裏付けされた議論ですが、コードに組織構造を提供するために名前空間が存在します。

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

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

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

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

73
user53019

私は同じ名前のクラスがたくさんあるが、クラスライブラリが異なる1つの会社で働いていました。

例えば、

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

悪いデザインだと思うかもしれませんが、これらが有機的にどのように発展するか、そして代替案は何であるか知っています。すべての主要なリファクタリング?

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

usingを直接上に置くと、これはarseの大きな問題でした ReSharper は、奇妙なことを実行して機能させ、usingディレクティブを書き換えますその場で、Customer-can-be-be-cast-as-Customer-styleエラーが発生し、どのタイプが正しいかわからなくなります。

したがって、少なくとも部分的な名前空間があると、

var cust = new Domain.Customer

または

public void Purchase(ViewModel.Customer customer)

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

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

17
Ewan

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

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.");
_

明らかに書くのに時間がかかりますが、デバッグに費やされる時間が減ります。

極端にプッシュすると、名前付き引数everywhereを使用するように強制できます。これには、doSomething(int, string)などの方法で混合する方法はありません。パラメータの順序。これはおそらく長期的にはプロジェクトに悪影響を及ぼし、前述のバグを防止するという利点のないコードがはるかに多くなります。

あなたの場合、「No using」ルールの背後にある動機は、_MyCompany.Something.List<T>_と_System.Collections.Generic.List<T>_のように、間違った型を使用することをより困難にするはずであるということです。

個人的には、私はそのようなルールを避けます。なぜなら、IMHO、コードを読み取るのが困難なコストは、間違った型を誤用するリスクがわずかに減少するメリットをはるかに上回りますが、少なくともこのルールには正当な理由があるからです。

7

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

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

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

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

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

   HeadquartersTeamLeader
   WarehouseTeamLeader

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

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

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

6
user147272

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

同様の問題がJavaの場合、たとえばimport Java.util.*;ではなくimport Java.util.List;を書き込むと発生します。後者の宣言では、List<>ソースの後半で発生し、その起源はimport宣言からわかります。

1