例外後にelseを使用する(または使用しない)


9

次のコードを検討してください。

if (x == 1)
{
  throw "no good; aborting" ;
}

[... more code ...]

今、このコードを考えてみましょう:

if (x == 1)
{
  throw "no good; aborting" ;
}
else
{
  [... more code ...]
}

2つのケースはまったく同じように機能します。最初のケースには、のコードの残りの部分を「囲む」必要がないという利点がありますelse。2番目の方法には、elsefor every を明示的に持つという慣例に従うという利点がありますif

誰かが一方を他方に有利に支持するような確かな議論を提供できますか?


5
あなたが明示的に支持して引用する慣習elseは偽物に思えます。多くの場合、else後ろに曲がらない限り、ブロックに入れるものはありません。

一貫性?コード変更に直面しても堅牢性を許可しますか?読みやすさ?
Thomas Eding、2012年

1
ええと、私はすべての人ifがを必要とするという考えのあまり好きではありませんelse。私たちのコードベースに取り組んだ最後のプログラマーがそれを厳格にフォローしました(まあ、時には...それは一種の統合失調症です)。結果として、else { /* do nothing */ }コードを
散らかす

4
「else for every if」は、(愚かな)一貫性の名のもとにクラウドアーキテクトによって発行された奇妙な宣言のようです。私はその慣習に従う利点はなく、どこでも聞いたことはありません。
エリックディートリッヒ

それは他に冗長です。.NETスタックを使用している場合は、ReSharperをインストールすると、冗長なelseステートメントをすべて削除する必要があります。
CodeART、2012年

回答:


16

a またはa を含むブランチなど、無条件に制御フローを中断するブランチのelse後に追加しないでください。これにより、ブランチによって導入される不要なネストのレベルが削除され、プログラムが読みやすくなります。ifthrowreturnelse

1つthrowの行で複数回のスローが行われると、シングルで大丈夫に見えるものは本当に醜くなります。

void myMethod(int arg1, int arg2, int arg3) {
    // This is demonstrably ugly - do not code like that!
    if (!isValid(arg1)) {
        throw new ArgumentException("arg1 is invalid");
    } else {
        if (!isValid(arg2)) {
            throw new ArgumentException("arg2 is invalid");
        } else {
            if (!isValid(arg3)) {
                throw new ArgumentException("arg3 is invalid");
            } else {
                // The useful code starts here
            }
        }
    }
}

このスニペットは同じことを行いますが、はるかに良く見えます:

void myMethod(int arg1, int arg2, int arg3) {
    if (!isValid(arg1)) {
        throw new ArgumentException("arg1 is invalid");
    }
    if (!isValid(arg2)) {
        throw new ArgumentException("arg2 is invalid");
    }
    if (!isValid(arg3)) {
        throw new ArgumentException("arg3 is invalid");
    }
    // The useful code starts here
}

+1正しい。2番目のOPケースでは、注意深く読んでから、WTFを残します。しかし...常にメソッドを短くするようにしてください。200行方式の途中でのリターンも悪い。
TulainsCórdova2012

1
公平に言うと、もしあなたが繰り返し使用しているなら、あなたはそうすることができますelse if
Guvante 2012年

2
@Guvante:それぞれifが単一の条件をテストし、条件がtrueの場合にそれを処理しelse ifます。条件がfalseの場合に発生する必要がある代替の事柄がない限り、sは不要です。dasblinkenlightの最初のスニペット「パチンコ機」のようなコードについては、オフィスの周りに用語があります。
Blrfl 2012年

@Blrfl pachinko machine hah、perfect analogy +1
Jimmy Hoffa

@Blrfl:繰り返しのifはネストが多すぎるという悪い例であることを言及していました。とにかく、ifを繰り返しネストするべきではありません。一般的に、あなたが少量のコードについて話しているのでない限り、含める理由はないことに同意しますelse
Guvante 2012年

5

ifのelseとして特別なケースのコードがないという事実を覆い隠すので、私はあなたがアンチパターンと呼ぶ "explicit else"プラクティスを呼び出します。

読みやすさ/保守性は一般に、必要なコードフロー構造だけがほとんどなく、それらを最小限にすると改善されます。つまり、elseが冗長であり、関数全体にスコープが追加されるifを使用すると、関数の追跡と維持が困難になります。

たとえば、次の関数があるとします。

public void ConfigureOblogon(Oblogon oblogonToConfigure)
{
    if (_validColors.Contains(oblogonToConfigure.Color))
    {
        oblogonToConfigure.ColorIndex = _validColors.IndexOf(oblogonToConfigure.Color);
    }
    else
    {
        oblogonToConfigure.Color = _validColors[0];
        oblogonToConfigure.ColorIndex = 0;
    }
}

要件は、構成時にoblogonのタイプ/タイプインデックスも指定する必要があるということです。誰かがそのコードを配置して、無効なコードで終わる可能性がある複数のスコープがあります。

public void ConfigureOblogon(Oblogon oblogonToConfigure)
{
    if (!_validOblogons.Contains(oblogonToConfigure.Type))
    {
        oblogonToConfigure.Type = _validOblogons[0];
        oblogonToConfigure.TypeIndex = 0;
        if (_validColors.Contains(oblogonToConfigure.Color))
        {
            oblogonToConfigure.ColorIndex = _validColors.IndexOf(oblogonToConfigure.Color);
        }
        else
        {
            oblogonToConfigure.Color = _validColors[0];
            oblogonToConfigure.ColorIndex = 0;
        }
    }
    else
    {
        oblogonToConfigure.TypeIndex = _validOblogons.IndexOf(oblogonToConfigure.Type);
    }
}

これを、元のコードが必要最小限の制御フロー構成で作成され、そのときに最小化された構成と比較してください。

public void ConfigureOblogon(Oblogon oblogonToConfigure)
{
    if (!_validColors.Contains(oblogonToConfigure.Color))
    {
        oblogonToConfigure.Color = _validColors[0];
    }

    oblogonToConfigure.ColorIndex = _validColors.IndexOf(oblogonToConfigure.Color);
}

誤ったスコープに誤って何かを入れたり、スコープを肥大化させたりして、この機能の長期的な成長と維持の重複を引き起こすことは、はるかに困難になります。加えて、この関数を通過する可能性のあるフローが明らかであるため、読みやすさが向上します。

私は知っています、例は少し工夫されていますが、私は何度も見ました

SomeFunction()
{
    if (isvalid)
    {
        /* ENTIRE FUNCTION */
    }
    /* Nothing should go here but something does on accident, and an invalid scenario is created. */
}

したがって、制御フロー構造に関するこれらのルールを形式化すると、人々がそのようなコードを書き始めたときに何かを嗅ぐために必要な直感を開発するのに役立つと思います。それから彼らは書き始めます。

SomeFunction()
{
    if (!isvalid)
    {
        /* Nothing should go here, and it's so small no one will likely accidentally put something here */
        return;
    }

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