web-dev-qa-db-ja.com

ループ内にネストされている場合、try-catchステートメントのネストは依然としてコードの匂いですか?

Try-catchステートメントをネストするとコードの臭いになることがよくあると聞いたので、この状況が例外かどうか疑問に思います。そうでない場合、リファクタリングするためのいくつかの良い方法は何でしょうか?

私のコードは次のようになります:

try{
    X x = blah;
    otherStuff;
    for (int i = 0; i < N; i++){
        try{
            String y = Integer.toString(i);
            f(x);
        }
        catch(Exceptions1 e1){
            System.err.println(... + y + ...);
            System.exit(0);
        }
}
catch(Exceptions2 e2){
    ...
}

ここでは、マルチキャッチを示すためにExceptionsを使用しています。

e2は、xを初期化してotherStuffを実行することによってスローされた例外をキャッチするために使用されます。理想的には、2つの行だけをtry-catchで囲んでいたはずですが、ループ内でxを使用し、try-catchの外側でnullに初期化することによって引き起こされる「未使用の割り当て」警告を回避したいと考えました。

e1e2でマルチキャッチされませんでした。反復の詳細に関する情報をユーザーに提供したいため、ループ内にキャッチブロックが必要だったからです。

8
Weasemunk

e2キャッチは、あなたが言うように、xを初期化し、otherStuffを実行する際のエラーをキャッチするためだけのものです。別のメソッドに抽出できます。これにより、ロジックが適切に分離され、otherStuffに意味のある名前を付けることができます。

public X Foo() {
    try{
        X x = blah;
        otherStuff;

        return x;
    }
    catch(Exceptions2 e2){
        ...
    }
}

public void Bar() {    
    X x = Foo();
    for (int i = 0; i < N; i++){
        try{
            String y = Integer.toString(i);
            f(x);
        }
        catch(Exceptions1 e1){
            System.err.println(... + y + ...);
            System.exit(0);
        }
    }
}
6
Martin Brenden

例外が発生したかどうかに関係なく、内部ループ全体を処理するつもりがない限り、コードは基本的に以下と同等です。

try{
    X x = blah;
    otherStuff;
    for (...){ 
       f(x)
    }
}
catch(Exceptions1 e1){
    ...
}
catch(Exceptions2 e2){
    ...
}

ネストは必要ありません。

それでも内部例外処理が必要な場合は、内部例外とそれを囲むメソッドを新しいメソッドにリファクタリングします。これには、内部ループを処理する他の方法について考えるように強いるという追加の利点もあります。例外の多くがスローされる場合、例外はパフォーマンスの面で非常に高価になる可能性があります。

7
Robert Harvey

私はあなたの質問についていくつかの観察があります:

  1. 指定した例では、例外はまったく必要ありません。文字列yを確認し、エラーをログに記録して、ループを終了するだけで済みます。 yが無効であり、ログに記録する必要があることについて、何も例外はありません。この場合に例外を使用しているという事実は、コードのにおいを示しています。

  2. マルチキャッチ例外は、実際には、大量のコードをラップし、問題が発生する可能性のあるものをキャッチするために使用されることを意図していません。これらは、コードの単一のセクションが一連の異なる例外を生成する可能性がある場合に、例外を区別するのに役立つことを目的としています。 「キャッチオール」アプローチの一種としてマルチキャッチ例外を使用しているため、これはコードのにおいを示しています。

  3. 要素の1つで例外が検出された場合でも、ループの残りの部分を繰り返し続けるつもりでない限り、ループ内にtry catchを配置しても意味がありません。サンプルコードは、1つのアイテムが例外的な場合にループを終了することを計画していることを示しています。

私はあなたが次のようなものでより良いかもしれないと思います:

try
{
     var x = blah;
     DoStuff();
     foreach(var i in icollection) // i is an int
     {
          var y = SomethingDependentOnI(i);

          // check y for explicit exceptional states
          if (IsSomethingWrongWithY(y))
               throw new Exception1(y);
     }
}
catch (Exception1 e1)
{
}
catch (Exception2 e2)
{
}

これはもっとはっきりしていますが、それでも上記の問題に悩まされています。

3
Price Jones