戻り値の計算とreturnステートメントを1行のメソッドに分割しますか?


26

私は同僚returnと、ステートメントと、戻り値を2行で計算するステートメントを壊すことについて議論しました。

例えば

private string GetFormattedValue()
{
    var formattedString = format != null ? string.Format(format, value) : value.ToString();
    return formattedString;
}

の代わりに

private string GetFormattedValue()
{
    return format != null ? string.Format(format, value) : value.ToString();
}

コードに関しては、最初のバリアントには値が実際には表示されません。私にとって、後者は、特に短い方法の場合、より明確です。彼の主張は、前者のバリアントの方がデバッグが簡単だということでした-VisualStudioではブレークポイントにより実行が停止したときにステートメントを非常に詳細に検査できるため、これは非常に小さなメリットです。

私の質問は、それでもデバッグを一見するのを簡単にするためだけに、あまり明確でないコードを書くのが有効なポイントであるなら?それ以上の引数があるため、分割計算とを有する変異体returnのステートメントは?


18
VSでは動作しませんが、複雑な式に条件付きブレークポイントを設定できない(または入力するのが複雑になる)と仮定すると、便宜上、おそらくassignとreturnを別々のステートメントに入れます。とにかく、コンパイラはおそらく両方について同じコードを思い付くでしょう。
トフロ

1
これは、特に変数がポインター動作の代わりに(おそらく複雑な)オブジェクトの動作を持つ言語では、言語に依存する可能性があります。@Paul Kの声明は、おそらくポインターの動作を持つ言語、オブジェクトが単純な値の動作を持つ言語、および成熟した高品質のコンパイラーを持つ言語に当てはまります。
MSalters

4
「ブレークポイントが原因で実行が停止した場合、VisualStudioによりステートメントの詳細な検査が可能になるため」-そうです。関数が複数のメンバーを持つ構造体を返した場合、どのようにして戻り値を取得しますか?(そして、その機能のサポートはせいぜい斑点があり、戻り値をまったく取得しない組み合わせがたくさんあります)。
Voo

2
誰かが今日使用しているデバッガで「ステートメントの非常に詳細な検査」を行うと、どのデバッガでも簡単にデバッグできるようにコードを書くのが悪いオプションになりますか?
イアン

16
全体の関数本体を減らすことによって、さらに彼を困らせるprivate string GetFormattedValue() => string.Format(format ?? "{0}", value);
グラハム

回答:


46

説明変数の導入はよく知られたリファクタリングであり、複雑な式を読みやすくするのに役立つことがあります。ただし、示されているケースでは、

  • 追加の変数は、周囲のメソッド名から明らかでないものを「説明」しません
  • ステートメントがさらに長くなるため、(わずかに)読みにくくなります

さらに、Visual Studioデバッガーの新しいバージョンでは、ほとんどの場合、余分な変数を導入することなく関数の戻り値を表示できます(ただし、注意が必要です。この古いSOの投稿とさまざまな回答をご覧ください)。

この特定のケースでは、私はあなたに同意しますが、説明変数が実際にコード品質を改善できる他のケースがあります。


私は間違いなくそこにいることを、あまりにも、同意していることは、間違いなく便利だした例が。
ポールケルシャー

2
通常result、変数の名前として使用します。そんなに長く、簡単にデバッグする
edc65

26
@ edc65:result 多くの場合、一般的な名前はコードにノイズを追加するだけで、読みやすさはほとんど向上しません。これがまさに私の答えのポイントです。これは、デバッグに役立つコンテキストで正当化できますが、別の変数を必要としないデバッガーを使用する場合は避けます。
Doc Brown

6
@JonHanna wayツールは私によれば長い。名前resultは、これが関数の結果の値であるという情報を伝えるため、関数が戻る前に確認できます。
edc65

1
@ edc65しかしそれはそれが便利に見えるようになります。だから今、私があなたのコードを読んでいるとき、私はすぐにそれがそうでないことに気付かない。そのため、コードは読みにくくなりました。
ジョンハンナ

38

事実を考えると:

a)コンパイラーが変数を最適化するため、最終コードに影響はありません。

b)分離することにより、デバッグ機能が強化されます。

個人的には、99%の確率でそれらを分離するのが良い習慣であるという結論に達しました。

この方法で行うことによる重大な欠点はありません。コードが肥大化するという議論は誤った呼び名です。なぜなら、肥大化したコードは、判読不能またはデバッグが困難なコードと比べると些細な問題だからです。さらに、このメソッドだけでは混乱するコードを作成することはできません。それは開発者次第です。


9
これは私にとって正しい答えです。これにより、デバッグ時にブレークポイントの設定と値の確認が容易になり、気づいている欠点はありません。
マシュージェームスブリッグス

ポイントbについては、Visual Studio Codeで、リターンにブレークポイントを置き、式GetFormattedValue()を追加するだけで、ブレークポイントにヒットしたときに結果が表示されるため、余分な行は不要です。ただし、デバッガに追加の式を追加する必要がないため、余分な行でローカルを表示する方が簡単です。だから、本当に個人的な好みの問題。
ジョンレイナー

3
戻り値の@JonRaynorは、関数の閉じ括弧にブレークポイントを置きます。複数の戻り値がある関数であっても、戻り値をキャッチします。
ボールドリック

16

多くの場合、ある結果に名前を付けるためだけに変数を導入すると、コードをより自己文書化するときに非常に役立ちます。この場合、変数名はメソッド名に非常に似ているため、これは要因ではありません。

1行のメソッドには固有の値がないことに注意してください。変更によってより多くの行が導入されてもコードが明確になる場合、それは良い変更です。

しかし、一般的に、これらの決定は個人の好みに大きく依存します。たとえば、条件演算子が不必要に使用されているため、両方のソリューションが混乱していることがわかります。私はif文を好んでいました。しかし、あなたのチームでは、異なる規約に同意しているかもしれません。それからあなたの慣例が示唆することは何でもしてください。このようなケースで規約が黙っている場合、これは長期的には問題ではない非常に小さな変更であることに注意してください。このパターンが繰り返し発生する場合は、チームとしてこれらのケースをどのように処理するかについてディスカッションを開始することができます。しかし、それは「良いコード」と「おそらく少し良いコード」に髪を分けています。


1
「条件演算子が不必要に使用されているため、両方のソリューションが混乱していることがわかりました。」—それは現実世界の例ではなく、ただ何かをすばやく作り上げなければなりませんでした。確かにこれは最良の例ではないかもしれません。
ポールケルチャー

4
本質的にこれが「レーダーの下」の違いであると言うための+1(他のことは等しい)気にすることは価値がありません。
TripeHound

3
@Mindwin、三項演算子を使用するときは、通常、複数の行に分割して、真の場合と偽の場合を明確にします。
アルトゥーロトレスサンチェス

2
@ArturoTorresSánchez私もそれを行うが、その代わりに?および:Iを使用if() {して} else {- - - - \\ :)
Mindwin

3
@Mindwin、私は行うことができない私は(オブジェクト初期化子のような)式の途中にいること
アルトゥーロ・トレス・サンチェス

2

ご質問への回答:

私の質問は、それでもデバッグを一見するのを簡単にするためだけに、あまり明確でないコードを書くのが有効なポイントであるなら?

はい。 実際には、以前の文は少し近視眼的であることを(悪気)私には思わないの一部(下記の太字を参照)、「かつての変種は、デバッグが容易であるということでした一切彼の議論- 非常に小さいメリットであります VisualStudioをするので、実行がブレークポイントのために停止しているときに、私たちのステートメントの非常に詳細な検査を可能にします。

デバッグを簡単にすることは(ほとんど)決して「小さなメリット」ではありません。なぜなら、ある推定では、プログラマーの時間の50%がデバッグに費やされているからです(リバーシブルデバッグソフトウェア)に。

分割計算とreturnステートメントを使用したバリアントの引数はありますか?

はい。 一部の開発者は、分割計算の方が読みやすいと主張するでしょう。もちろん、これはデバッグに役立ちますが、コードが実行または適用する可能性のあるビジネスルールを解読しようとしている場合にも役立ちます。

注:ビジネスルールは頻繁に変更される可能性があるため、データベースでより適切に処理される場合があります。それにもかかわらず、この分野での明確なコーディングは依然として最優先事項です。(ビジネスルールエンジンの構築方法


1

さらに先に進みます。

private string GetFormattedValue()
{
    if (format != null) {
        formattedString = string.Format(format, value);
    } else {
        formattedString = value.ToString()
    }
    return formattedString;
}

どうして?

より複雑なロジックに三項演算子を使用するのは読みにくいため、より複雑なステートメントには上記のようなスタイルを使用します。このスタイルを常に使用することにより、コードは一貫性があり、人間が解析しやすくなります。さらに、この種の一貫性を導入することで(およびコードのリントと他のテストを使用することで)回避できますgoto fail型エラー。

もう1つの利点は、コードカバレッジレポートが、formatnull以外のテストを含めるのを忘れた場合に通知することです。これは三項演算子には当てはまりません。


私が推奨する代替手段-メソッドからの複数のリターンに対してではなく、「可能な限り迅速にリターンを得る」場合:

private string GetFormattedValue()
{
    if (format != null) {
        return string.Format(format, value);
    }

    return value.ToString();
}

したがって、最後の戻り値を見て、デフォルトが何であるかを確認できます。

ただし、一貫性を保つことが重要です。そして、すべてのメソッドがいずれかの規則に従うようにします。


1
最初の例はvalue.ToString()、形式がnullでないときに不必要に呼び出されるため、悪い習慣のようです。一般的な場合、これには重要な計算が含まれ、フォーマット文字列を含むバージョンよりも時間がかかる場合があります。たとえば、valuePIを小数点以下100万桁まで保存する、および最初の数桁のみを要求するフォーマット文字列を考えてください。
スティーブ

1
なぜprivate string GetFormattedValue() => string.Format(format ?? "{0}", value); 同じではないのか、デバッガーに依存するのではなく、単体テストを使用して正確性を確認してください。
ベリンロリチュ

1
私は、3進法あまり明確でないことに同意しますが、nullターミネーターは物事をより明確にすることができます。少なくともこの場合。
ベリンロリチュ

1
親愛なる日記、今日、よく知られた(約40年間存在する)パラダイム、イディオム、および演算子を使用して、明確で簡潔なコードを書くことは、引用、二重引用は巧妙な二重引用、非引用ではなく、代わりに過度に冗長なコードを書くことを読みましたDRYで、前述の演算子、イディオム、パラダイムを使用せず、プログラミングのバックグラウンドがなく、5歳の人にしかわからないようなものを避けようとする代わりに、明快です。地獄、私は本当に古い、私の親愛なる日記になったに違いありません...
vaxquis

1
「より複雑なロジックに三項演算子を使用することは判読できません」実際にはそうですが(そして、私はロジックを過度に複雑にしているのを見たことがあります)、これはOPのコードには当てはまらず、三項演算子に固有のことでもありません。自信を持って言える唯一のことは、ラインが長すぎるということです。gist.github.com/milleniumbug/cf9b62cac32a07899378feef6c06c776は、私がそれを再フォーマットする方法です。
ミレニアムバグ

1

このような手法は、デバッグの必要性によって正当化できるとは思いません。私はこのアプローチに何千回も遭遇し、時々これをやり続けていますが、マーティン・ファウラーがデバッグについて言ったことを常に覚えています:

人々はデバッグに費やす時間も過小評価しています。彼らは、長いバグを追いかけるのに費やすことができる時間を過小評価しています。テストでは、バグを追加したことがすぐにわかります。これにより、バグをすぐに修正してから、クロールして非表示にすることができます。デバッグよりもイライラする、または時間を浪費するものはほとんどありません。そもそもバグを作成しなかった場合、それは非常に速くなるのではないでしょうか?


マーティン・ファウラーは賢い人で、私は彼の(そしてあなたの)意見を読むのを楽しんだ。私はテストが必要であり、その努力により多くの時間が費やされるべきであると固く信じていますが、私たちはすべて誤りやすい人間であるという事実は、テストの量がすべてのバグを根絶しないことを示唆します。したがって、デバッグは常にプログラム開発およびサポートプロセスの一部になります。
tale852150

1

一部の人々は、三項演算子のように、質問に接する問題にこだわっていると思います。はい、多くの人がそれを嫌うので、とにかく育てるのがいいかもしれません。

あなたの質問の焦点に関しては、返されたステートメントを移動して、変数によって参照されるようにします...

この質問は、私が同意しない2つの仮定を作ります:

  1. 2番目のバリアントがより明確または読みやすい(逆のことが言える)

  2. 誰もがVisual Studioを使用していること。私は何度もVisual Studioを使用してきましたが、それでも問題なく使用できますが、通常は別のものを使用しています。特定のIDEを強制する開発環境は、懐疑的です。

名前付き変数に何かを分割することで、読みにくくなることはめったにありません。ほとんど常に逆のことが行われます。誰かがそれを行う特定の方法は問題を引き起こす可能性があります。たとえば、自己文書の大君がvar thisVariableIsTheFormattedResultAndWillBeTheReturnValue = ...それを行う場合、明らかにそれは悪いことですが、それは別の問題です。var formattedText = ...結構です。

この特定の場合、およびおそらく1ライナーについて説明しているため、多くの場合、変数は関数名がまだ伝えていないことをあまり伝えません。したがって、変数はそれほど追加しません。デバッグ引数はまだ保持できますが、この特定のケースでは、デバッグ時に焦点となる可能性のあるものは表示されません。誰かが何らかの方法でデバッグなどのためにそのフォーマットを必要とする場合、後で簡単に変更できます

一般的に、あなたは一般的なルールを要求しました(あなたの例は、一般化されたフォームの例でした)、バリアント1(2ライナー)を支持するすべてのポイントは正しいです。これらは良いガイドラインです。しかし、ガイドラインには柔軟性が必要です。たとえば、私が取り組んでいるプロジェクトでは、1行あたり最大80文字なので、多くの行を分割しますが、通常、81〜85文字の行を見つけます。限界。

値を追加する可能性は低いため、特定の例では2行は実行しません。この場合、他の方法で行うにはポイントの強度が十分でないため、バリアント2(1ライナー)を使用します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.