ネストを減らすために「if」ステートメントを反転します


272

私のコードでReSharperを実行したとき、たとえば:

    if (some condition)
    {
        Some code...            
    }

ReSharperは私に上記の警告(ネストを減らすために「if」ステートメントを反転する)を与え、次の修正を提案しました:

   if (!some condition) return;
   Some code...

それが良い理由を理解したいと思います。メソッドの途中で「return」を使用すると、「goto」のように問題があるといつも思っていました。


1
例外のチェックと最初からの戻りは問題ないと思いますが、条件を変更して、例外をチェックするのではなく、直接チェックするようにします(つまり、(何らかの条件)が戻る場合)。
bruceatk 2008

36
いいえ、パフォーマンスには影響しません。
セスカーネギー

3
代わりにメソッドに不正なデータが渡された場合は、ArgumentExceptionをスローしたくなるでしょう。
asawyer

1
@asawyerはい、ここでは、アサーションエラーを使用するのではなく、ナンセンスな入力を許容しすぎる関数についての全体的な議論があります。確かなコードを書くことは、これに私の目を開きました。その場合、これはのようなものになりASSERT( exampleParam > 0 )ます。
グレッグヘンダーショット

4
アサーションはパラメータではなく内部状態用です。まず、パラメーターを検証し、内部状態が正しいことを表明してから、操作を実行します。リリースビルドでは、アサーションを省略するか、コンポーネントのシャットダウンにマップできます。
Simon Richter、2011

回答:


296

メソッドの途中でのリターンは必ずしも悪いことではありません。コードの意図がより明確になったら、すぐに戻る方が良いでしょう。例えば:

double getPayAmount() {
    double result;
    if (_isDead) result = deadAmount();
    else {
        if (_isSeparated) result = separatedAmount();
        else {
            if (_isRetired) result = retiredAmount();
            else result = normalPayAmount();
        };
    }
     return result;
};

この場合、_isDeadtrue であれば、すぐにメソッドから抜けることができます。代わりに、次のように構成する方がよい場合があります。

double getPayAmount() {
    if (_isDead)      return deadAmount();
    if (_isSeparated) return separatedAmount();
    if (_isRetired)   return retiredAmount();

    return normalPayAmount();
};   

私はこのコードをリファクタリングカタログから選びました。この特定のリファクタリングは次のように呼ばれます:入れ子の条件付きをガード句で置き換える。


13
これは本当に良い例です!リファクタリングされたコードは、caseステートメントのように読み取られます。
その他、

16
おそらく単に好みの問題です。読みやすさをさらに向上させるために、2番目と3番目の「if」を「else if」に変更することをお勧めします。「return」ステートメントを見落としても、次のケースは前のケースが失敗した場合にのみチェックされること、つまりチェックの順序が重要であることは明らかです。
2008年

2
この単純な例では、idは2番目の方法の方が良いことに同意しますが、それは何が起こっているのか明らかだからです。
Andrew Bullock

では、コードジョプが書いたきれいなコードを取り、Visual Studioがそれを分解して別の行にリターンを配置しないようにするにはどうすればよいでしょうか。それがコードをそのように再フォーマットすることは本当に私を不快にさせます。この非常に読みやすいコードを醜くします。
マークT

@Mark T Visual Studioには、コードが分割されないようにする設定があります。
アーロンスミス

333

これは美的であるだけでなく、メソッド内の最大ネストレベルも削減します。これは、メソッドを理解しやすくするため、一般にプラスと見なされます(実際、多くの 静的 分析 ツールは、これをコード品質の指標の1つとして測定します)。

一方、それはまたあなたのメソッドに複数の出口点を持たせ、別の人々のグループがノーノーであると信じているものです。

個人的には、ReSharperと最初のグループに同意します(例外がある言語では、「複数の出口点」について議論するのはばかげています。ほとんど何でも投げることができるため、すべてのメソッドに潜在的な出口点が数多くあります)。

パフォーマンスに関して:すべての言語で、両方のバージョンが同等である必要があります(ILレベルでない場合は、コードでジッターが解消された後は確かです)。理論的にはこれはコンパイラーに依存しますが、実際に今日広く使用されているコンパイラーは、これよりもはるかに高度なコード最適化ケースを処理できます。


41
シングル出口ポイント?誰が必要ですか?
sq33G 2011

3
@ sq33G:SESEに関するその質問(そしてもちろん答えも)は素晴らしいです。リンクをありがとう!
Jon

単一の出口点を支持する人々がいるという回答を聞き続けていますが、特にC#のような言語で、実際にそれを支持する人を見たことはありません
Thomas Bonini、2011

1
@AndreasBonini:証明の不在は不在の証明ではありません。:-)
Jon

うん、もちろん、私は)誰もが別のアプローチのような一部の人々は、これらの人々は=それ自体を言う必要性を感じていない場合と言うする必要性を感じているという奇妙な見つける
トーマスBonini

102

これは少し宗教的な議論ですが、ネストを少なくした方がよいというReSharperに同意します。これは、関数からの複数の戻りパスがあることの欠点を上回ると思います。

ネストが少ない主な理由は、コードの可読性と保守性を向上させるためです。他の多くの開発者は将来的にコードを読む必要があり、インデントが少ないコードは一般にはるかに読みやすいことを覚えておいてください。

前提条件は、関数の開始時に早期に戻ることが許可される良い例です。残りの関数の読みやすさが、前提条件チェックの存在によって影響を受けるのはなぜですか?

メソッドから複数回戻ることの欠点については、デバッガーは非常に強力になり、特定の関数がどこでいつ戻るかを簡単に正確に見つけることができます。

関数に複数の戻りがあることは、メンテナンスプログラマの仕事に影響を与えません。

コードの可読性が低下します。


2
関数の複数の戻り値は、メンテナンスプログラマに影響します。関数をデバッグするときに、不正な戻り値を探す場合、メンテナーは、復帰できるすべての場所にブレークポイントを配置する必要があります。
EvilTeach 2008年

10
開始ブレースにブレークポイントを設定します。関数をステップ実行すると、チェックが順番に実行されていることがわかるだけでなく、その実行で関数がどのチェックに到達したかが明確になります。
John Dunagan

3
ここでジョンに同意します...関数全体をステップ実行するだけでは問題は発生しません。
Nailer

5
@Nailer非常に遅く、特に同意します。関数が大きすぎてステップスルーできない場合は、とにかく複数の関数に分割する必要があります。
アイディアカピ

1
ジョンにも同意します。多分それは、ブレークポイントを下に置くと考えられていた古い学校ですが、関数が何を返しているのか知りたい場合は、関数が何を返しているのかを確認するために、その関数をステップ実行します。何が返ってくるのかを見たいだけなら、最後の括弧に入れてください。
user441521 2017年

70

他の人が述べたように、パフォーマンスへの影響はありませんが、他の考慮事項があります。これらの有効な懸念事項は別として、これはまた、状況によっては取り掛かる可能性があります。あなたがdouble代わりに扱っていたとしましょう:

public void myfunction(double exampleParam){
    if(exampleParam > 0){
        //Body will *not* be executed if Double.IsNan(exampleParam)
    }
}

一見同等の反転とは対照的です:

public void myfunction(double exampleParam){
    if(exampleParam <= 0)
        return;
    //Body *will* be executed if Double.IsNan(exampleParam)
}

そのため、特定の状況では、正しく反転しているように見えるものも反転しifない場合があります。


4
また、resharperクイックフィックスはexampleParam> 0をexampleParam <0に反転し、exampleParam <= 0ではないことに注意してください。
2014

1
そして、これは、そのチェックが前にあり、開発者がナンが救済に帰すべきであると開発者が考えると同様に返されるべきである理由です。
user441521 2017年

51

関数の最後でのみ戻るという考えは、言語が例外をサポートする前の時代から生まれました。これにより、プログラムはメソッドの最後にクリーンアップコードを配置できることに依存し、それが呼び出され、他のプログラマーがクリーンアップコードをスキップする原因となったメソッドの戻りを隠さないようにすることができます。 。クリーンアップコードをスキップすると、メモリまたはリソースリークが発生する可能性があります。

ただし、例外をサポートする言語では、そのような保証はありません。例外をサポートする言語では、ステートメントまたは式を実行すると、制御フローが発生し、メソッドが終了する可能性があります。つまり、finallyまたはキーワードを使用してクリーンアップを実行する必要があります。

とにかく、多くの人がなぜ「メソッドの最後に戻るだけ」のガイドラインを引用するのは、なぜそれが良いことだったのか理解せずに説明していると思います。


6
例外がUglyAndEvil [tm]である理由を理解したところです;-)例外は、豪華で高価な変装のゴットです。
EricSchaefer、2008年

16
@Ericあなたは非常に悪いコードにさらされていたに違いありません。それらが誤って使用されている場合は非常に明白であり、通常は高品質のコードを記述できます。
ロバートポールソン、

これが本当に探していた答えです!ここには素晴らしい答えがありますが、それらのほとんどは例に焦点を当てており、この推奨事項が生まれた実際の理由ではありません。
Gustavo Mori、

これが最良の答えです。この議論全体がそもそもなぜ生じたのかを説明しているからです。
バトルButkus 2013年

If you've got deep nesting, maybe your function is trying to do too many things.=>これは前のフレーズからの正しい結果ではありません。コードCのビヘイビアAをコードDのビヘイビアAにリファクタリングできると言った直前です。コードDの方がわかりやすく、許可されていますが、「多すぎる」とは、変更されていないビヘイビアを指します。したがって、この結論には意味がありません。
v.oddou 2014

30

逆転した場合のための名前があることを追加したいと思います-ガード句。できる限り使用します。

私は、最初にコードの2つの画面があり、他にない場合にコードを読むのが嫌いです。ちょうど反転して戻ります。そうすれば、スクロールに時間を無駄にする人がいなくなります。

http://c2.com/cgi/wiki?GuardClause


2
丁度。よりリファクタリングです。あなたが言及したように、それはコードをより簡単に読むのを助けます。
rpattabi

'return'は、guard句にとって十分ではなく、恐ろしいものではありません。私はそうします:if(ex <= 0)throw WrongParamValueEx( "[MethodName] Bad input param 1 value {0} ...そして、あなたは例外をキャッチしてアプリログに書き込む必要があります
JohnJohnGa

3
@ジョン。これが実際にエラーである場合、あなたは正しいです。しかし、多くの場合そうではありません。そして、メソッドが呼び出されるすべての場所で何かをチェックする代わりに、メソッドはそれをチェックして何もせずに戻ります。
Piotr Perak、2011

3
コード、ピリオド、ifsまたはno の2画面のメソッドを読むのは嫌です。笑
jpmc26 14

1
私は個人的には、まさにこの理由から(また、ネストを避けるために)早期の返品を好みます。ただし、これはパフォーマンスに関する元の質問とは無関係であり、おそらくコメントになるはずです。
Patrick M

22

それは美学に影響を与えるだけでなく、コードの入れ子化も防ぎます。

実際にデータが有効であることを保証する前提条件として機能することもできます。


18

これはもちろん主観的ですが、次の2つの点で大幅に改善されると思います。

  • これで、関数がcondition保持されている場合、何もすることが残っていないことがすぐにわかります。
  • 入れ子のレベルを低く抑えます。ネストすると、思った以上に読みやすさが損なわれます。

15

複数の戻り点は、C(および程度は低いがC ++)では問題でした。これは、戻り点のそれぞれの前にクリーンアップコードを複製することを強制したためです。ガベージコレクションでは、try| finally構築し、usingブロックする場合、それらを恐れるべき理由は本当にありません。

結局のところ、それはあなたとあなたの同僚が読みやすくするものに帰着します。


それだけではありません。物事を掃除するような実際的な考慮事項とは何の関係もない疑似コードに言及する学問的な理由があります。この非医学的な理由は、命令構造の基本形式を尊重することと関係があります。ループ出口を中央に配置しないのと同じ方法です。このようにして、不変条件を厳密に検出し、動作を証明できます。または終了を証明することができます。
v.oddou 2014

1
ニュース:実際、私は早期のブレイクとリターンが悪いという非常に実用的な理由を見つけました。それは、私が言った静的分析の直接的な結果です。さあ、インテルC ++コンパイラの使用ガイドペーパー:d3f8ykwhia686p.cloudfront.net/1live/intel/…です。キーフレーズ:a loop that is not vectorizable due to a second data-dependent exit
v.oddou 2014年

12

パフォーマンスに関しては、2つのアプローチの間に顕著な違いはありません。

しかし、コーディングはパフォーマンス以上のものです。明確さと保守性も非常に重要です。そして、パフォーマンスに影響を与えないこのようなケースでは、重要なのはそれだけです。

どちらのアプローチが望ましいかについて、競合する学派があります。

1つのビューは、他の人が言及したビューです。2番目のアプローチは、ネストレベルを減らし、コードの明確性を向上させます。これは命令型のスタイルでは自然です。何もすることがなくなったときは、早めに戻ることもできます。

より機能的なスタイルの観点から見たもう1つの見方は、メソッドには出口点が1つだけあるべきだということです。関数型言語のすべては式です。したがって、ifステートメントには常にelse句が必要です。それ以外の場合、if式は常に値を持つとは限りません。したがって、機能的なスタイルでは、最初のアプローチがより自然です。


11

ガード句または前提条件(おそらくご覧のとおり)は、特定の条件が満たされているかどうかを確認し、プログラムのフローを中断します。これらは、ifステートメントの1つの結果だけに本当に関心がある場所に最適です。だから言うよりも:

if (something) {
    // a lot of indented code
}

条件を逆転させ、その逆転条件が満たされた場合に中断する

if (!something) return false; // or another value to show your other code the function did not execute

// all the code from before, save a lot of tabs

returnほど汚いところはありませんgoto。値を渡して、関数が実行できなかった残りのコードを示すことができます。

ネストされた条件でこれを適用できる最良の例が表示されます。

if (something) {
    do-something();
    if (something-else) {
        do-another-thing();
    } else {
        do-something-else();
    }
}

対:

if (!something) return;
do-something();

if (!something-else) return do-something-else();
do-another-thing();

最初の方がきれいだと主張する人はほとんどいないでしょうが、もちろん、それは完全に主観的なものです。一部のプログラマーは、インデントによって何かが動作している条件を知りたいのですが、メソッドフローを線形に保ちたいと思っています。

preconsがあなたの人生を変えるか、またはあなたを解雇することを一瞬提案しませんが、あなたはあなたのコードを少しだけ読みやすくするかもしれません。


6
私はそのとき数少ない人の一人だと思います。最初のバージョンの方が読みやすい。ネストされたifsにより、決定木がより明確になります。一方で、いくつかの前提条件がある場合は、それらをすべて関数の先頭に配置することをお勧めします。
その他、

@Otherside:完全に同意します。シリアル形式で記述されたリターンにより、脳は可能なパスをシリアル化する必要があります。if-treeがいくつかのトランジエントロジックに直接マッピングできる場合、たとえば、lispにコンパイルできます。ただし、シリアルリターン方式では、コンパイラーの実行がはるかに困難になります。ここでのポイントは、明らかにこの仮想シナリオとは関係ありません。コード分析、最適化の機会、修正の証明、終了の証明、および不変条件の検出と関係があります。
v.oddou 2014

1
ネストの数が多いほど、コードを読みやすくなることは誰にもわかりません。何かのようなブロックが多いほど、一目で読みやすくなります。ここで最初の例を読みやすくする方法はありません。実際のこのようなコードのほとんどがより深くなり、すべてのネストで従うにはより多くの脳力が必要な場合にのみ、2つのネストになります。
user441521

1
入れ子が2倍の深さである場合、「「何か他のことをするのはいつですか」という質問に答えるのにどのくらいかかりますか?ペンと紙なしでは答えられないでしょう。しかし、それがすべて守護句である場合は、かなり簡単に答えることができます。
David Storfer

9

ここでいくつかの良い点がありますが、メソッドが非常に長い場合、複数の戻り点も読みにくくなる可能性があります。つまり、複数のリターンポイントを使用する場合は、メソッドが短いことを確認してください。そうしないと、複数のリターンポイントの読みやすさのボーナスが失われる可能性があります。


8

パフォーマンスは2つの部分に分かれています。ソフトウェアの運用中にパフォーマンスが得られますが、開発とデバッグ中にもパフォーマンスが必要です。開発者が最後に望むのは、些細なことを「待つ」ことです。最後に、最適化を有効にしてこれをコンパイルすると、同様のコードが生成されます。したがって、両方のシナリオで効果を発揮するこれらの小さなトリックを知っておくとよいでしょう。

問題のケースは明らかです、ReSharperは正しいです。ifステートメントをネストしてコードに新しいスコープを作成するのではなく、メソッドの最初に明確なルールを設定します。読みやすさが向上し、保守が容易になり、行きたい場所を見つけるためにふるいにかけなければならないルールの量が減ります。


7

個人的に私は出口ポイントを1つだけ好む。メソッドを短く適切な位置に保つと、簡単に達成でき、コードを次に作業する人に予測可能なパターンを提供します。

例えば。

 bool PerformDefaultOperation()
 {
      bool succeeded = false;

      DataStructure defaultParameters;
      if ((defaultParameters = this.GetApplicationDefaults()) != null)
      {
           succeeded = this.DoSomething(defaultParameters);
      }

      return succeeded;
 }

これは、関数が終了する前に関数内の特定のローカル変数の値を確認するだけの場合にも非常に役立ちます。あなたがする必要があるのは、最後のリターンにブレークポイントを置くことだけであり、あなたはそれをヒットすることが保証されます(例外がスローされない限り)。


4
bool PerformDefaultOperation(){DataStructure defaultParameters = this.GetApplicationDefaults(); return(defaultParameters!= NULL && this.DoSomething(defaultParameters);}そこで、修正しました。:)
tchen

1
この例は非常に基本的であり、このパターンの要点を逃しています。この関数の内部で他に4つのチェックを行ってから、より読みやすいことを伝えます。
user441521

5

コードがどのように見えるかについての多くの正当な理由。しかし、結果はどうですか?

C#コードとそのILコンパイル済みフォームを見てみましょう。


using System;

public class Test {
    public static void Main(string[] args) {
        if (args.Length == 0) return;
        if ((args.Length+2)/3 == 5) return;
        Console.WriteLine("hey!!!");
    }
}

この単純なスニペットはコンパイルできます。生成された.exeファイルをildasmで開き、結果を確認できます。私はすべてのアセンブラのことを投稿することはしませんが、結果を説明します。

生成されたILコードは次のことを行います。

  1. 最初の条件が偽の場合、2番目の条件があるコードにジャンプします。
  2. trueの場合、最後の命令にジャンプします。(注:最後の命令は戻りです)。
  3. 2番目の条件では、結果の計算後に同じことが起こります。比較:falseの場合はConsole.WriteLineに、trueの場合は最後に取得します。
  4. メッセージを印刷して戻ります。

したがって、コードは最後にジャンプするようです。ネストされたコードで通常の場合はどうなりますか?

using System;

public class Test {
    public static void Main(string[] args) {
        if (args.Length != 0 && (args.Length+2)/3 != 5) 
        {
            Console.WriteLine("hey!!!");
        }
    }
}

結果は、IL命令で非常によく似ています。違いは、条件ごとにジャンプする前に、falseの場合は次のコードに進み、trueの場合は終了するという点です。そして今、ILコードはより良く流れ、3つのジャンプがあります(コンパイラはこれを少し最適化しました):1.最初のジャンプ:Lengthが0のとき、コードが最後にジャンプする(3番目のジャンプ)部分まで。2. 2番目:2番目の条件の途中で、1つの命令を回避します。3. 3番目:2番目の条件がfalseの場合、最後にジャンプします。

とにかく、プログラムカウンターは常にジャンプします。


5
これは良い情報ですが、個人的には、コードを管理するつもりの人にはILが表示されないので、ILが「上手く流れる」と私はそれほど気にできませんでした
JohnIdol

1
C#コンパイラの次の更新での最適化パスが、現在の状況と比較してどのように機能するかは、実際には予想できません。個人的には、ILで異なる結果を生成するためにC#コードを微調整しようとする時間を費やしたりはしません。 。それでも、他のオプションについてはもっと厳しく思います。同じ静脈では、私は...あなたはJITコンパイラが各プラットフォームのためにそれに供給されますILをやっているの最適化のどのような種類の任意のアイデアを持っていることを疑う
クレイグ

5

理論的には、反転ifにより分岐予測のヒット率が向上すると、パフォーマンスが向上する可能性があります。実際には、特にコンパイル後にブランチ予測がどのように動作するかを正確に知ることは非常に難しいと思うので、アセンブリコードを記述している場合を除いて、日常の開発ではそれを行いません。

分岐予測の詳細はこちら


4

それは単に論争の的です。早期復帰の問題については、「プログラマー間の合意」はありません。私の知る限り、それは常に主観的です。

ほとんどの場合はtrueになるように条件を記述しておく方がよいため、パフォーマンスを議論することは可能です。それはより明確であると主張することもできます。一方、ネストされたテストを作成します。

この質問に対する決定的な答えは得られないと思います。


私はあなたのパフォーマンスの議論を理解していません。条件はブール値なので、結果がどうであれ、常に2つの結果があります...ステートメントを逆にする(またはしない)と結果が変わる理由はわかりません。(条件に「NOT」を追加すると、測定可能な量の処理が追加されると言わない限り...
Oli

2
最適化は次のように機能します。最も一般的なケースがelseブロックではなくifブロックであることを確認した場合、CPUは通常、パイプラインに読み込まれたifブロックのステートメントを既に持っています。条件を返しますfalseの場合、CPUは、そのパイプラインを空にする必要があり、...
Otherside

読みやすさのためだけに他の人が言っていることをするのも好きです。「else」ではなく「then」ブロックに否定的なケースがあるのは嫌です。CPUの動作を知らない、または考慮しなくても、これを行うのは理にかなっています
Andrew Bullock

3

すでに多くの洞察に満ちた答えがありますが、それでも、私は少し異なる状況に向けます:前提条件の代わりに、それは確かに関数の上に置かれるべきです、ステップバイステップの初期化を考えてください。成功するために各ステップを確認してから、次のステップに進む必要があります。この場合、上部のすべてをチェックすることはできません。

ネスティングパラダイムに従っていたため、SteinbergのASIOSDKを使用してASIOホストアプリケーションを作成すると、コードが本当に読みにくくなっていました。それは8レベルの深さのようになり、上記のAndrew Bullockによって言及されたように、私はそこに設計上の欠陥を見ることはできません。もちろん、一部の内部コードを別の関数にパックし、残りのレベルをネストして読みやすくすることもできますが、これはかなりランダムに見えます。

ネストをガード句に置き換えることで、クリーンアップコードの一部ではなく、関数の最後ではなく、関数内で発生するはずだった部分に関する誤解さえも発見しました。入れ子になったブランチがあれば、私はそれを見たことはなかったでしょう。

したがって、これは、逆転したifがより明確なコードに貢献できる別の状況である可能性があります。


3

複数の出口点を避けることができ、パフォーマンスの向上につながります。C#についてはわかりませんが、C ++では、名前付き戻り値の最適化(コピーエリシオン、ISO C ++ '03 12.8 / 15)は単一の出口点を持つことに依存しています。この最適化により、コピーによる戻り値の作成が回避されます(特定の例では問題ではありません)。これにより、関数が呼び出されるたびにコンストラクタとデストラクタが保存されるため、タイトループでのパフォーマンスが大幅に向上する可能性があります。

しかし、99%のケースでは、追加のコンストラクターとデストラクターの呼び出しを保存しても、ネストされたifブロックがもたらす読みやすさを損なう価値はありません(他の人が指摘したように)。


2
そこ。最後に、NRVOについて言及している人を見つけるために、同じことを尋ねる3つの質問(そのうち2つは凍結されている)で数十の回答を3回長く読みました。ねえ…ありがとう
v.oddou 2017年

2

それは意見の問題です。

私の通常のアプローチは、単一行のifを避け、メソッドの途中で戻ることです。

メソッドのあらゆる場所で示唆されているような行は必要ありませんが、メソッドの上部にある一連の仮定をチェックし、すべてが合格した場合にのみ実際の作業を行うということは言うべきことです。


2

私の意見では、単にvoid(またはチェックしない無駄な戻りコード)を返している場合は、初期の戻りで問題ありません。ネストを回避すると同時に、関数が完了したことを明示的にするため、読みやすさが向上する可能性があります。

実際にreturnValueを返す場合-通常はネストがより良い方法であり、returnValueを1か所(最後)で返すため、多くの場合にコードの保守が容易になります。


1

確かではありませんが、R#は飛躍的なジャンプを回避しようとしていると思います。IF-ELSEがある場合、コンパイラーは次のようなことを行います。

条件false->はるかにfalse_condition_labelにジャンプ

true_condition_label:命令1 ...命令n

false_condition_label:命令1 ...命令n

エンドブロック

条件がtrueの場合、ジャンプもロールアウトL1キャッシュもありませんが、false_condition_labelへのジャンプは非常に遠く離れている可能性があり、プロセッサは独自のキャッシュをロールアウトする必要があります。キャッシュの同期は高価です。R#は、遠いジャンプを短いジャンプに置き換えようとします。この場合、すべての命令がすでにキャッシュにある可能性が高くなります。


0

それはあなたが何を好むかに依存すると思います、前述のように、一般的な合意はありません。煩わしさを減らすために、この種の警告を「ヒント」に減らすことができます


0

私の考えは、「関数の途中」の戻りはそれほど「主観的」であってはならないということです。理由は非常に簡単です。次のコードを見てください。

    function do_something(data){

      if(!is_valid_data(data)) 
            falseを返します。


       do_something_that_take_an_hour(data);

       istance = new object_with_very_painful_constructor(data);

          if(インスタンスが無効です){
               エラーメッセージ( );
                戻る;

          }
       connect_to_database();
       get_some_other_data();
       戻る;
    }

たぶん最初の「復帰」は直感的ではないかもしれませんが、それは本当に節約しています。クリーンなコードに関する「アイデア」が多すぎて、「主観的な」悪いアイデアを失うには、もっと練習が必要です。


例外的な言語では、これらの問題はありません。
user441521

0

この種のコーディングにはいくつかの利点がありますが、私にとって大きな利点は、すばやく戻ることができれば、アプリケーションの速度を向上できることです。IE前提条件Xのため、エラーですばやく戻ることができることはわかっています。これにより、最初にエラーケースが取り除かれ、コードの複雑さが軽減されます。多くの場合、CPUパイプラインがよりクリーンになるため、パイプラインのクラッシュやスイッチを停止できます。第2に、ループしている場合、中断したり、すぐに戻ったりすると、CPUを大幅に節約できます。一部のプログラマーは、ループの不変式を使用してこの種の迅速な終了を行いますが、この場合、CPUパイプラインを壊し、メモリシークの問題を引き起こし、CPUが外部キャッシュからロードする必要があることを意味します。でも基本的には意図したことをすべきだと思います つまり、ループまたは関数が終了しても、正しいコードの抽象的な概念を実装するためだけに複雑なコードパスが作成されません。あなたが持っている唯一の道具がハンマーなら、すべてが釘のように見えます。


グラフの答えを読むと、ネストされたコードは、実行されたコードが複数のリターンを使用するよりも高速になるようにコンパイラーによって最適化されたように聞こえます。ただし、複数のリターンがより高速であったとしても、この最適化はおそらくアプリケーションをそれほど高速化しません... :)
hangy

1
同意しません-第一法則はアルゴリズムを正しくすることです。しかし、私たちはすべてエンジニアであり、それは私たちが持っているリソースで正しい問題を解決し、利用可能な予算で実行することです。遅すぎるアプリケーションは目的に適していません。
デビッドアランフィンチ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.