次のコードを想像してください:
void DoThis()
{
if (!isValid) return;
DoThat();
}
void DoThat() {
Console.WriteLine("DoThat()");
}
Voidメソッド内でreturnを使用しても大丈夫ですか?パフォーマンスにペナルティはありますか?または、次のようなコードを書く方が良いでしょう:
void DoThis()
{
if (isValid)
{
DoThat();
}
}
Voidメソッドでの戻りは悪くありません。 入れ子を減らすためにif
ステートメントを反転する の一般的な方法です。
また、メソッドのネストを少なくすると、コードの可読性と保守性が向上します。
実際、returnステートメントのないvoidメソッドがある場合、コンパイラは常に末尾に ret命令 を生成します。
(ネストされたコードとは対照的に)ガードを使用するもう1つの大きな理由があります。別のプログラマーが関数にコードを追加する場合、彼らはより安全な環境で作業しています。
考慮してください:
void MyFunc(object obj)
{
if (obj != null)
{
obj.DoSomething();
}
}
対:
void MyFunc(object obj)
{
if (obj == null)
return;
obj.DoSomething();
}
次に、別のプログラマーが次の行を追加することを想像してください:obj.DoSomethingElse();
void MyFunc(object obj)
{
if (obj != null)
{
obj.DoSomething();
}
obj.DoSomethingElse();
}
void MyFunc(object obj)
{
if (obj == null)
return;
obj.DoSomething();
obj.DoSomethingElse();
}
明らかにこれは単純なケースですが、プログラマーは最初の(ネストされたコード)インスタンスのプログラムにクラッシュを追加しました。 2番目の例(ガード付きの早期終了)では、ガードを通過すると、コードは意図せずにnull参照を使用することがなくなります。
確かに、優れたプログラマーはこのような間違いを犯しません(多くの場合)。しかし、予防は治療よりも優れています。この潜在的なエラーの原因を完全に排除する方法でコードを書くことができます。ネストは複雑さを増すため、ベストプラクティスでは、コードをリファクタリングしてネストを減らすことをお勧めします。
悪い練習???とんでもない。実際、検証が失敗した場合は、できるだけ早くメソッドから戻ることにより、常に検証を処理することをお勧めします。それ以外の場合は、大量のネストされたifとelseになります。早期に終了すると、コードの可読性が向上します。
同様の質問の回答も確認してください。 if-elseの代わりにreturn/continueステートメントを使用する必要がありますか?
それは悪い習慣ではありません(すでに述べたすべての理由のため)。ただし、メソッドのリターンが多いほど、より小さな論理メソッドに分割する必要があります。
最初の例は、guardステートメントを使用しています。から ウィキペディア :
コンピュータプログラミングでは、ガードはブール式であり、問題のブランチでプログラムの実行を続行する場合はtrueと評価する必要があります。
メソッドの先頭に多数のガードを配置することは、完全に理解できる方法です。基本的に「これらのいずれかが真である場合、このメソッドを実行しないでください」と言っています。
したがって、一般的には次のようになります。
void DoThis()
{
if (guard1) return;
if (guard2) return;
...
if (guardN) return;
DoThat();
}
それはもっと読みやすいと思います:
void DoThis()
{
if (guard1 && guard2 && guard3)
{
DoThat();
}
}
パフォーマンスの低下はありませんが、2番目のコードは読みやすいため、保守が容易です。
それは完全に大丈夫であり、「パフォーマンスのペナルティ」はありませんが、括弧のない「if」ステートメントを決して記述しないでください。
常に
if( foo ){
return;
}
より読みやすくなりました。また、コードの一部がそうではないときにそのステートメント内にあると誤って想定することはありません。
この場合、2番目の例はより良いコードですが、それはvoid関数から戻ることとは何の関係もありません。2番目のコードがより直接的なためです。ただし、void関数から戻ることはまったく問題ありません。
私は、これに関する若い皆さんのホイッパースナッパーすべてに反対します。
無効かどうかにかかわらず、メソッドの途中でreturnを使用することは非常に悪い習慣です。理由は、40年ほど前に、有名な "GOTO Statement有害であると考えられている" 」、およびDahl、Dijkstra、Hoareによる「Structured Programming」に続きます。
基本的なルールは、すべての制御構造、およびすべてのモジュールに、厳密に1つのエントリと1つの出口があることです。モジュールの真ん中に明示的に戻ると、その規則が破られ、プログラムの状態について推論することがはるかに難しくなり、プログラムが正しいかどうかを判断するのがはるかに難しくなります(これははるかに強力なプロパティです) 「動作するように見えるかどうか」より)。
「有害と考えられるGOTOステートメント」と「構造化プログラミング」は、1970年代の「構造化プログラミング」革命を開始しました。これらの2つの部分が、if-then-else、while-do、およびその他の明示的な制御構造を今日持っている理由であり、高水準言語のGOTOステートメントが絶滅危Spec種リストに含まれている理由です。 (私の個人的な意見では、絶滅種リストに登録する必要があります。)
Message Flow Modulatorは、最初の試行で受け入れテストに合格した最初の軍事ソフトウェアであり、逸脱、権利放棄、または「ええ、しかし」という言い回しはなく、言語さえも書かれていないことは注目に値しますGOTOステートメント。
また、Nicklaus Wirthは、Oberonプログラミング言語の最新バージョンであるOberon-07のRETURNステートメントのセマンティクスを変更し、型付けされたプロシージャ(関数)の宣言の末尾部分に変更したことにも言及する価値があります。関数の本体にある実行可能ステートメント。彼の変更の説明は、以前の形式[〜#〜]が[〜#〜]構造化プログラミングの1つの出口の原則に違反しているため、まさにそれを行ったと述べています。