C#では、#region
/ #endregion
キーワードを使用して、エディターでコードの領域を折りたたみ可能にすることができます。これを行うたびに、おそらく他のクラスやメソッドにリファクタリングされる可能性のある大きなコードの塊を隠すためにそれを行います。たとえば、管理しやすくするために、3つまたは4つの領域を持つ500行のコードを含むメソッドを見てきました。
地域の賢明な使用は問題の兆候でしょうか?私にはそう思われます。
C#では、#region
/ #endregion
キーワードを使用して、エディターでコードの領域を折りたたみ可能にすることができます。これを行うたびに、おそらく他のクラスやメソッドにリファクタリングされる可能性のある大きなコードの塊を隠すためにそれを行います。たとえば、管理しやすくするために、3つまたは4つの領域を持つ500行のコードを含むメソッドを見てきました。
地域の賢明な使用は問題の兆候でしょうか?私にはそう思われます。
回答:
コードにおいは、潜在的にバグの数を増やす可能性のあるデザインに問題があることを示す症状です。これはリージョンの場合ではありませんが、リージョンは長いメソッドのようにコードの匂いの作成に寄与する可能性があります。
以来:
アンチパターン(またはアンチパターン)とは、一般的に使用される可能性のあるソーシャルまたはビジネスオペレーションまたはソフトウェアエンジニアリングで使用されるパターンですが、実際には非効率的および/または逆効果です
領域はアンチパターンです。コードの品質や可読性を向上させたり、バグの数を減らしたりすることなく、コードのリファクタリングをより複雑にするだけの、より多くの作業が必要です。
メソッドは短くなければなりません。メソッドに10行しかない場合、他の5行で作業するときに、5行を非表示にするためにリージョンを使用することはおそらくないでしょう。
また、各メソッドは1つだけのことを行う必要があります。一方、リージョンは異なるものを分離することを目的としています。メソッドがAを実行し、次にBを実行する場合、2つの領域を作成することは論理的ですが、これは間違ったアプローチです。代わりに、メソッドを2つの個別のメソッドにリファクタリングする必要があります。
この場合にリージョンを使用すると、リファクタリングがさらに難しくなる可能性があります。あなたが持っていると想像してください:
private void DoSomething()
{
var data = LoadData();
#region Work with database
var verification = VerifySomething();
if (!verification)
{
throw new DataCorruptedException();
}
Do(data);
DoSomethingElse(data);
#endregion
#region Audit
var auditEngine = InitializeAuditEngine();
auditEngine.Submit(data);
#endregion
}
最初の領域を折りたたんで2番目の領域に集中するのは危険であるだけでなく、フローを停止する例外を簡単に忘れることができます(return
代わりにガード句があり、それを見つけるのはさらに困難です)。この方法でコードをリファクタリングする必要がある場合:
private void DoSomething()
{
var data = LoadData();
#region Work with database
var verification = VerifySomething();
var info = DoSomethingElse(data);
if (verification)
{
Do(data);
}
#endregion
#region Audit
var auditEngine = InitializeAuditEngine(info);
auditEngine.Submit(
verification ? new AcceptedDataAudit(data) : new CorruptedDataAudit(data));
#endregion
}
現在、リージョンは意味をなさないため、最初のリージョンのコードを見なくても、2番目のリージョンのコードを読んで理解することはできません。
私が時々見る別のケースはこれです:
public void DoSomething(string a, int b)
{
#region Validation of arguments
if (a == null)
{
throw new ArgumentNullException("a");
}
if (b <= 0)
{
throw new ArgumentOutOfScopeException("b", ...);
}
#endregion
#region Do real work
...
#endregion
}
引数の検証が数十のLOCにまたがり始めたときに領域を使用するのは魅力的ですが、この問題を解決するためのより良い方法があります。
public void DoSomething(string a, int b)
{
if (a == null)
{
throw new ArgumentNullException("a");
}
if (b <= 0)
{
throw new ArgumentOutOfScopeException("b", ...);
}
InternalDoSomething(a, b);
}
private void InternalDoSomething(string a, int b)
{
...
}
一部の人々はそれらを使用してフィールド、プロパティなどをグループ化します。このアプローチは間違っています。コードがStyleCopに準拠している場合、フィールド、プロパティ、プライベートメソッド、コンストラクタなどはすでにグループ化されており、簡単に見つけることができます。そうでない場合は、コードベース全体で均一性を確保するルールを適用することを検討してください。
他の人々は地域を使用して多くの同様の実体を隠します。たとえば、100個のフィールド(コメントと空白を数えると少なくとも500行のコードが作成される)を持つクラスがある場合、それらのフィールドを領域内に配置し、折りたたんで、忘れてしまいがちです。繰り返しますが、あなたはそれを間違っています:クラスに非常に多くのフィールドがあるので、継承を使用するか、オブジェクトをいくつかのオブジェクトにスライスすることを考える必要があります。
最後に、一部の人々は、関連するものをグループ化するために領域を使用するように誘惑されます:デリゲートを伴うイベント、またはIOに関連する他のメソッドを伴うIOに関連するメソッドなど。 、読んで理解する。2番目のケースでは、おそらく複数のクラスを作成する方が適切です。
いいえ。従来の使用法:生成されたコードがありました。それでも、コード生成ツールは、代わりに部分クラスを使用する必要があります。C#にリージョンサポートがある場合、これは主にこのレガシー使用であり、コードでリージョンを使用する人が多すぎるため、既存のコードベースを壊さずにそれらを削除することは不可能だからです。
について考えてみてくださいgoto
。言語またはIDEが機能をサポートしているという事実は、その機能を毎日使用する必要があるという意味ではありません。StyleCop SA1124ルールは明確です。リージョンを使用しないでください。決して。
現在、同僚のコードのコードレビューを行っています。コードベースには多くのリージョンが含まれており、実際には、リージョンを使用しない方法と、リージョンが悪いコードにつながる理由の両方の完璧な例です。ここではいくつかの例を示します。
4000 LOCモンスター:
私は最近、using
Programs.SEのどこかで、ファイルに含まれるsの数が多すぎる場合(「Unused Usingsを削除」コマンドを実行した後)、このファイル内のクラスが多すぎることを示しています。同じことがファイル自体のサイズにも当てはまります。
コードを確認しているときに、4000 LOCファイルに出会いました。このコードの作成者は、同じ15行のメソッドを何百回もコピーアンドペーストし、変数の名前と呼び出されたメソッドをわずかに変更したように見えました。単純な正規表現では、ジェネリックをいくつか追加するだけで、ファイルを4000 LOCから500 LOCにトリミングできました。より巧妙なリファクタリングにより、このクラスは数十行に削減される可能性があると確信しています。
リージョンを使用することにより、著者は、コードを維持することが不可能であり、記述が不十分であるという事実を無視し、コードをリファクタリングするのではなく複製することを奨励しました。
地域「Do A」、地域「Do B」:
別の優れた例は、単純にタスク1、タスク2、タスク3などを実行するモンスター初期化メソッドです。完全に独立した5〜6つのタスクがあり、それぞれがコンテナクラスの何かを初期化します。これらのタスクはすべて1つのメソッドにグループ化され、リージョンにグループ化されました。
これには1つの利点がありました。
一方、問題は複数ありました。
リージョン間に依存関係があるかどうかは明らかではありませんでした。うまくいけば、変数の再利用はありませんでした。そうしないと、メンテナンスがさらに悪夢になる可能性があります。
メソッドをテストすることはほぼ不可能でした。一度に20のことを行うメソッドがそれらを正しく行う場合、どのように簡単にわかりますか?
フィールド領域、プロパティ領域、コンストラクター領域:
また、レビューされたコードには、すべてのフィールド、すべてのプロパティなどをグループ化した多くの領域が含まれていました。これには、ソースコードの増加という明らかな問題がありました。
ファイルを開いてフィールドの膨大なリストを見ると、最初にクラスをリファクタリングしてからコードを操作する傾向が強くなります。地域では、物を折りたたんでそれを忘れる習慣があります。
別の問題は、どこでもそれを行うと、1ブロックの領域を作成することに気付くということです。これは意味がありません。これは、実際に私がレビューしたコードの場合で、#region Constructor
1つのコンストラクターがたくさん含まれています。
最後に、フィールド、プロパティ、コンストラクターなどはすでに順番に並んでいるはずです。それらが慣例(大文字で始まる定数など)に一致する場合、要素のタイプがどこで停止し、他の要素が始まるかはすでに明らかなので、そのための領域を明示的に作成する必要はありません。
多くの人が地域をそれほど情熱的に憎んでいるのは信じられないことです!
私は彼らの多くの異議に完全に同意します:コードをa #region
に押し込んでビューから隠すことは悪いことです。クラスを#regions
個別のクラスにリファクタリングする必要がある場合に分割することは明らかに間違いです。を使用し#region
て冗長なセマンティック情報を埋め込むことは、冗長です。
しかし、これらのことは、コードで領域を使用することに本質的に問題があることを意味するものではありません!ほとんどの人々の反対は、他の人がこのようなIDE機能を誤って使用する傾向があるチームで働いたことに起因すると推測できます。プライマリーを自分で作業する贅沢があり、地域が私のワークフローを整理するのに役立っていることに感謝しています。強迫神経症のせいかもしれませんが、どれほどきちんとエレガントに書かれていても、一度にたくさんのコードを画面に表示するのは好きではありません。物事を論理的な領域に分けることで、自分がやろうとしているコードで作業することに関心のないコードを折りたたむことができます気にします。私は不適切に記述されたコードを無視しているわけではありません。それ以上リファクタリングする意味はありません。
C ++での作業により多くの時間を費やし、Windows APIに直接プログラミングするようになったので、地域のサポートがC#のサポートと同じくらい良いことを望んでいます。別のGUIライブラリを使用すると、コードがより簡単または明確になり、無関係なコードノイズを画面から取り除く必要がなくなると主張できますが、そうしないことには別の理由があります。キーボードとIDEに十分習熟しているため、リージョンに細分化されたコードの展開/折りたたみに要する時間は1秒未満です。私が意識的に集中しているのは、現在取り組んでいるコードだけに制限しようとする、頭の力で節約する時間です。すべてが単一のクラス/ファイルに属しますが、すべてが同時に画面に属しているわけではありません。
ポイントは#regions
、コードを分離して論理的に分割することは、どんな犠牲を払っても避けるべき悪いことではないということです。エドが指摘するように、それは「コード臭」ではありません。コードの匂いがする場合、それはその地域からではなく、その地域に埋めようとしたコードからであると確信できます。機能がより整理されたり、より良いコードを書くのに役立つなら、私はそれを使うと言います。それが邪魔になったり、間違って使用していることに気付いた場合は、使用を中止してください。最悪の事態が発生し、それを使用する人々とチームで作業することを余儀なくされた場合、キーボードショートカットを記憶して、コードのアウトラインをオフにします:Ctrl+ M、Ctrl+P。文句を言うのを止めてください。これが、「真の」「ハードコア」プログラマーとして見られたいと思う人たちが自分自身を証明しようとする別の方法だと感じることがあります。構文の色付けを避けるよりも、領域を避けるほうがましです。それはあなたをよりマッチョな開発者にするわけではありません。
そうは言っても、メソッド内の領域はまったくナンセンスです。それをやりたいと思うときはいつでも、別のメソッドにリファクタリングする必要があります。言い訳しない。
まず、「コードのにおい」という言葉にもう耐えられません。それはあまりにも頻繁に使用され、噛み付くと良いコードを認識できなかった人々によって投げられます。いずれかの方法...
私は個人的に多くの地域を使うのが好きではありません。コードを取得するのが難しくなり、コードは私が興味を持っているものです。私は、頻繁に変更する必要のないコードの大部分があるとき、リージョンが好きです。それを除けば、彼らは私の邪魔をしているように見え、「プライベートメソッド」、「パブリックメソッド」などの地域は私を夢中にさせます。彼らはさまざまなコメントに似ていますi++ //increment i
。
また、リージョンの使用は、テキストエディタのレイアウトではなく、プログラムロジック/デザインパターンを記述するために一般的に使用されるため、実際には「アンチパターン」にはなり得ないことを付け加えます。これは主観的です。あなたのために働くものを使用してください。アンチパターンがすべての目的である領域の過剰使用のために、保守不能なプログラムになることは決してありません。:)
はい、地域はコードの匂いです!
コンパイラからリージョンが完全に削除されるのを見てうれしいです。すべての開発者は、他のプログラマーにとって決して価値のない独自の無意味なグルーミングスキームを考え出します。本当の価値とは関係なく、自分の赤ちゃんを飾り付けて美しくしたいプログラマと関係があります。
あなたが「そうだ、私の同僚がここでいくつかの地域を使っていたらいいのに!」
すべてのリージョンを自動的に拡張するようにIDEを構成することはできますが、それでも目障りであり、実際のコードの読み取りを妨げます。
すべての公開メソッドがまとめられているかどうかは、あまり気になりません。おめでとうございます。変数宣言と初期化の違いを知っています。コードで表示する必要はありません!
価値のないグルーミング!
さらに、リージョンの使用によってファイルに「情報アーキテクチャ」が必要な場合は、コアの問題に対処することができます。クラスが大きすぎます!それを小さな部分に分割することははるかに有益であり、適切に行われると真の意味論/読みやすさが追加されます。
個人的には、さまざまなタイプのメソッドやコードの一部をグループ化する方法としてリージョンを使用しています。
したがって、コードファイルは、開くと次のようになります。
メソッド内に領域を配置しません。私見はコード臭のサインです。私はかつて1200行以上の長さで、5つの異なる領域を持つメソッドに出会いました。怖い光景でした!
他の開発者がより速く物を見つけられるようにコードを整理する方法としてそれを使用している場合、私はそれが問題の兆候だとは思わない。メソッド内のコード行を隠すためにそれを使用している場合、そのメソッドを再考する時が来たと思います。
#region
ブロックを使用して非常に大きなクラスを読みやすくすることは、通常、単一責任原則に違反する兆候です。グループ行動に使用されている場合、クラスがあまりにも多くのことを実行している可能性もあります(もう一度SRPに違反しています)。
「コードの匂い」の考え方に固執すると、#region
ブロックはそれ自体ではコードの匂いではありませんが、匂いを隠そうとするという点で、より「コードのFebreze」です。私は過去にそれらを大量に使用していましたが、リファクタリングを開始すると、隠れていないため、表示が少なくなります。
ここでのキーワードは「賢明」です。メソッド内に領域を置くことが賢明な場合を想像するのは困難です。それはコード隠蔽と怠inessである可能性が高いです。ただし、コードのあちこちにいくつかの領域があることには、十分な理由があります。
たくさんの地域がある場合、それはコードのにおいだと思います。リージョンは、多くの場合、将来のリファクタリングの可能な場所のヒントです。多くの地域は、誰かが実際にヒントを得ていないことを意味します。
賢明に使用すると、多くのメソッドを持つ単一のクラスの構造と、それぞれに少数のメソッドがあるだけの多くのクラスの構造の間の良い中間点を提供します。これらは、クラスが複数のクラスにリファクタリングされるべきポイントに近づき始めたときに最も便利ですが、まだそこにはありません。関連するメソッドをグループ化することで、数が増え続けている場合に、関連するメソッドのセットを独自のクラスに後で簡単に抽出できるようにします。たとえば、500行のコードに近づいているクラスがある場合、ある領域で収集された合計200行のコードを使用するメソッドのセットは、おそらく何らかの形でリファクタリングするのに適しています。メソッドも良いターゲットかもしれません。
私が領域を使用するもう1つの方法は、大規模なメソッドをリファクタリングすることのマイナスの影響の1つを減らすことです。多くの小さく、簡潔で、簡単に再利用できるメソッドです。リージョンは、メソッドとそのヘルパーを読者にメタカプセル化する良い方法です。そのため、クラスの異なる側面で作業している人は、それらを折りたたんで、コードのその部分をすぐに却下できます。もちろん、これは、リージョンが非常によく整理されていて、コードをドキュメント化する別の方法として基本的に使用されている場合にのみ機能します。
一般的に、リージョンを使用すると、リージョンを使用しない場合よりもはるかに早く、自分自身を整理し、コードを「文書化」し、リファクタリングする場所を見つけるのに役立ちます。
主にCRUDサーバークラスの領域を使用して、さまざまな種類の操作を整理します。それでも、私は喜んで彼らなしで行くことができました。
広範囲に使用すると、レッドフラグが発生します。私は、あまりにも多くの責任を負うクラスに目を光らせています。
私の経験では、コードに数百行あるメソッドは間違いなく臭いです。
あなたは地域をお持ちの場合はINコードあなたは確かに(生成されたコードの場合を除けば。)問題を抱えているコード内の領域を置くことは基本的に「これをリファクタリング。」と言っています
ただし、他の場合もあります。しばらく前に思いついたのは、事前に計算されたアイテムが数千あるテーブルです。これはジオメトリの説明であり、テーブルにエラーがなければ、それを見る機会は決してありません。もちろん、リソースなどからデータを取得することもできますが、コンパイラーを使用して読みやすくすることはできません。
最近のプロジェクトでは、いくつかの領域が埋め込まれた1700行メソッドがありました。興味深いのは、メソッド内で実行されていた明確なアクションを領域が区別したことです。コードの機能に影響を与えることなく、各領域でリファクタリング->抽出メソッドを実行できました。
一般に、ボイラープレートコードを隠すために使用される領域は便利です。プロパティやフィールドなどを非表示にするためにリージョンを使用することはお勧めしません。クラス内で作業するときに見づらい場合は、クラスをさらに分解する必要がある兆候だからです。しかし、ハードルールとして、メソッド内に領域を配置する場合、そのブロックを領域にラップするよりも、何が起こっているのかを説明する別のメソッドを抽出した方がよいでしょう。
リージョンは、高品質のコードで使用できますか?恐らく。多くの場合、彼らはそうだと思います。しかし、私の個人的な経験から私は非常に疑わしいままです-私はほとんど排他的に誤用された地域を見てきました。私はうんざりしていますが、それでも楽観的です。
これまで見てきた地域を使用したコードを、3つのカテゴリに大まかに分けることができます。
因数分解が不十分なコード:私が見たコードのほとんどは、貧しい人々の因数分解ツールとしてリージョンを使用しています。たとえば、さまざまな目的に特化することが理にかなっているクラスに成長したクラスは、代わりに、目的ごとに1つの別々の領域に分割される場合があります。
問題ドメイン用に間違ったライブラリ、時には間違った言語を使用して記述されたコード多くの場合、プログラマーが問題ドメインに適切なライブラリのセットを使用していないと、コードが信じられないほど冗長になります-多くの小さなヘルパー関数実際には属していない(おそらく独自のライブラリに属しています)。
学生または最近の卒業生によって書かれたコード。一部のプログラムとコースでは、あらゆる種類の奇妙な目的のために地域を使用して生徒に教えようとしています。リージョンタグとコードの行の比率が1:5以下の範囲になるまで、ソースコードを散らかすリージョンが表示されます。
「コードのにおい」だと思います。
アンチパターンは一般的にソフトウェアの基本的な構造上の問題ですが、リージョン自体はエディターで不快な動作を引き起こすだけです。リージョンを使用することは、本質的に悪いことではありませんが、特にコードのチャンクを隠すために頻繁に使用することは、他の独立した、より大きな問題が他の場所で起こっていることを示します。
領域を使用するのは1つのことだけです(少なくとも、使用する他の場所は考えられません)。メソッドの単体テストをグループ化するためです。
通常、クラスごとに1つのテストクラスがあり、メソッドの名前を持つ領域を使用して、各メソッドの単体テストをグループ化します。それがコードの匂いなのか何かわからないが、基本的な考え方は、コード内の何かが変わったために壊れない限りユニットテストを変更する必要がないということなので、特定のメソッドのすべてのテストを見つけやすくするかなり早く。
過去にコードを整理するためにリージョンを使用したことがありますが、前回やったことを思い出せません。ただし、ユニットテストクラスでは地域にこだわっています。
領域を使用して、可視性とメンバータイプの各組み合わせを含めます。したがって、すべてのプライベート関数は領域などに移動します。
これを行う理由は、コードを折りたたむことができるからではありません。これは、プロキシへの参照を挿入できるようにエディターをスクリプト化したためです。
#region "private_static_members"
/// <summary>
/// cache for LauncherProxy
/// </summary>
private static LauncherProxy _launcherProxy;
#endregion
#region "protected_const_properties"
protected LauncherProxy LauncherProxy{
get{
if(_launcherProxy==null) {
if (!God.Iam.HasProxy(LauncherProxy.NAME)) {
God.Iam.RegisterProxy(new LauncherProxy());
}
_launcherProxy=God.Iam.Proxy(LauncherProxy.NAME) as LauncherProxy;
}
return _launcherProxy;
}
}
#endregion
コードに入れ、各部分を適切な領域にきちんとはめ込みます。
この場合、マクロはプロジェクトを分析し、プロキシのリストボックスを提供して、必要なコードを挿入します。カーソルが動かない。
C#の学習の初めに、共通性を維持するためにリージョンを使用することを検討していましたが、それは常に1対1の関係ではないため、ヒットとミスの命題です。2つの地域で使用されているメンバーを心配したい人、またはそれらの条件で物事を分割し始めたい人。
他のタイプの分離はメソッドのみです。メソッドをコマンド、関数、ハンドラーに分割します。そのため、パブリック、プライベートなどのコマンド用の領域があります。
これにより粒度が決まりますが、一貫性があり明確な粒度で信頼できます。
領域はプリプロセッサ式です。つまり、コメントのように扱われ、コンパイラによって基本的に無視されます。これらは、Visual Studioで使用される純粋なビジュアルツールです。したがって、#regionは実際にはコード臭ではありません。コードではないからです。コードのにおいはむしろ800などのメソッドであり、さまざまな責任が組み込まれています。したがって、1つのメソッドに10の領域がある場合、おそらくコードのにおいを隠すために使用されています。非常に効果的に使用されているクラスを見たことがありますが、非常に効果的に使用されているクラスは、目を楽しませてナビゲートしやすくしています。
地域は気の利いた組織的なアイデアでしたが、すべてを過度に分類したい開発者の傾向を考慮に入れておらず、ほとんどの現代のOOPプラクティスでは一般的に不要です...あなたのクラス/メソッドがあまりにも大きく、あなたはおそらくSOLID原則の「S」に違反している...しかし、いずれの匂いのように、それはしないよう、リファクタリングすべきことは、必ずしも何かが悪い起こっている意味。
リージョンは、オブジェクト指向コードであるIMOよりも機能的なコードでより多くの目的に役立ちます。IMOでは、分割するのが理にかなっているシーケンシャルデータの長い関数を持っていますが、私はc#で個人的に使用したことがあり、ほとんど常に必要のない/見たくないコードに集中してください。私にとって、これらは通常NPocoまたはそのバリアントに使用されるコードベースの生のSQL文字列定数でした。ORMを介してデータがPOCOオブジェクトにどのように入力されるかを実際に気にしない限り、これらは完全に無意味でした...そして気にするなら、ちょっと地域とBAMを拡張するだけです!150行以上の複雑なSQLクエリが表示されます。